1 Getting Started

Welcome to the Whisker IDE — the design environment for the D6 Labs Nexus.io family of automation controllers. This chapter takes you from download to a first look at the workspace, introduces the handful of concepts the rest of the manual builds on, and points you to the hands-on walkthroughs.

1.1 About this manual

The Whisker IDE ships in two editions built from the same code:

This one manual covers both. Where a feature needs an account, the chapter says so; everything else — tags, ladder, HMI, alerts, Python, emulation, deploying — works the same in both editions.

1.2 System requirements

Minimum Recommended
Operating system Windows 10 (64-bit), version 1809 or later Windows 11
Processor 64-bit dual-core Quad-core or better
Memory 4 GB RAM 8 GB RAM
Disk space 1 GB free 2 GB free (projects, HMI runtime bundles)
Display 1280 × 720 1920 × 1080 or larger
Network Ethernet or Wi-Fi on the same LAN as your controller Same, plus internet access for the Cloud edition

The IDE itself runs without an internet connection. You need the LAN to reach your controllers, and the Cloud edition needs internet access to sign in and to talk to Whisker.io.

1.3 Downloading the IDE

D6 Labs supplies the IDE as a single zip file per edition, named whiskeride_v<version>_cloud_windows.zip or whiskeride_v<version>_standalone_windows.zip. Ask your D6 Labs contact for the edition you have licensed. The zip contains the installer plus four short text files (README, LICENSE, CHANGELOG, THIRD-PARTY-NOTICES) and the Quick Start PDF.

When the download completes, locate the file in your Downloads folder (or wherever your browser saves downloads).

Downloaded zip in File Explorer

Note. If your browser warns that the file might be dangerous, choose Keep. Windows SmartScreen can flag any installer it has not seen many times before. Check that the file came from D6 Labs, then continue.

1.4 Installing the IDE

1.4.1 Step 1 — Extract the zip

  1. Right-click the downloaded zip file.
  2. Choose Extract All…
  3. Accept the default destination and click Extract.
Windows Extract All dialog

The extracted folder contains:

File What it is
WhiskerIDE-Cloud-<version>-Setup.exe or WhiskerIDE-Standalone-<version>-Setup.exe The installer
README.txt Quick reference matching this chapter
LICENSE.txt End-user licence agreement
CHANGELOG.txt Notable changes in this release
THIRD-PARTY-NOTICES.txt Open-source component attributions
WhiskerIDE-QuickStart-v<version>.pdf The Quick Start guide

1.4.2 Step 2 — Run the installer

  1. Double-click the Setup.exe file.

    Note. Windows may show a “Windows protected your PC” warning because the installer was downloaded from the internet. Click More info → Run anyway. This appears before the User Account Control prompt in the next step.

  2. Windows asks for permission to make changes. Click Yes.

  3. The setup wizard appears. The defaults suit almost everyone:

    Screen What to do
    License Agreement Read it, choose I accept, click Next
    Select Additional Tasks Tick Create a desktop icon if you want one. Click Next
    Ready to Install Click Install
    Completing Setup Leave Launch Whisker IDE ticked. Click Finish
    Setup wizard ready-to-install screen
  4. When installation finishes the IDE launches.

1.4.3 Step 3 — Confirm the install

You should now have a Start menu entry named Whisker PLC IDE (Cloud edition) or Whisker PLC IDE Standalone (Offline edition), a desktop shortcut if you chose one, and an entry under Settings → Apps. The IDE is installed under Program Files in a folder of the same name. You can delete the zip and the extracted folder.

The installer also places the HMI runtime, the I/O scanner service and the PLC compiler alongside the IDE; you do not install those separately.

1.5 First launch

1.5.1 Cloud edition — sign in

The Cloud edition opens on a sign-in screen with Email, Password, a Remember me box, Sign In, and below a divider, Continue Offline.

Sign In does two things. It authenticates you with Whisker.io, and it fetches your IDE certificate — the credential a managed controller checks before it accepts your connection or your deploy. You do not set up certificates by hand; signing in once per PC is enough. If your account uses two-factor authentication, a Two-Factor Authentication screen asks for the Verification Code from your authenticator app.

Continue Offline skips the account. You get the same workspace with the cloud features hidden; you can still emulate, connect to a standalone controller (or a managed one you hold a site identity for) and deploy. Sign in later from the Cloud indicator in the status bar.

Note. Remember me keeps your e-mail address and password in Windows-protected storage, encrypted for your Windows user account, so the IDE can sign you in next time. Another Windows account on the same PC cannot read them, but anyone using your account can open the IDE signed in — do not tick it on a shared login. (Earlier releases kept them in plain files in Documents; the first start of this release moves them into protected storage and deletes the files.)

1.5.2 Offline edition

The Offline edition has no sign-in screen; it opens straight to the workspace.

1.5.3 The first workspace

Either way, you arrive at an empty workspace with two buttons in the editor area: New Application and Open Application.

Whisker IDE on first launch, empty workspace

On first launch the IDE also creates your default project folder, DocumentsProjects, and copies two finished sample projects into it: walkthrough_ladder.widez (the Quick Start’s motor scenario in ladder logic) and walkthrough_py.widez (the same scenario in Python). Open Application starts in that folder, so opening a sample is a two-click way to see a complete project.

The window title tells you which edition you are running: Whisker PLC IDE or Whisker PLC IDE Standalone. When an Application is open, the title shows its file name; a leading * means there are unsaved changes.

1.6 Core concepts

Application — the file you open and save. An Application is a .widez archive that holds one or more Projects plus an Application-level Design Spec and a little metadata (customer, site, notes). A single-controller installation is an Application with one Project; a controller with an operator PC is an Application with two.

Project — one program for one controller (or one PC application). A Project has a Target, a Tag Database, and — depending on the Target — ladder programs and tasks, I/O configuration, HMI screens, alerts and Python files. Projects live inside the Application archive; they are not separate files on disk. See Projects and Applications.

Target — the kind of hardware a Project runs on. The Target Hardware dropdown offers three:

Target Hardware in the IDE describes each in detail.

Tag — a named variable. Tags have a type — BOOL, INT (16-bit), DINT (32-bit) or REAL — and an address in controller memory: %MX for BOOL, %MW for INT, %MD for DINT, %MF for REAL; physical inputs are %I… and outputs %Q…. Ladder logic, HMI widgets, alerts, the Modbus map and Python all refer to tags by name. See Tag Database.

Task — the schedule a ladder program runs on. There are three kinds: Cyclic (every N milliseconds — the Main task every new project starts with is cyclic at 10 ms), Event (once, when a chosen memory bit changes on a rising, falling or either edge) and Freewheel (as fast as the controller allows, for logic that paces itself). Tasks appear under Tasks in the Project Tree. See Function Blocks, Faceplates and Tasks.

Device — a piece of external I/O the controller talks to: a Modbus RTU module on RS-485, a Modbus TCP device, another Nexus.io controller (ArenaTCP) or an EtherNet/IP PLC. Adding a device creates its tags for you. See I/O Configuration and Devices.

Design Spec — a Markdown document attached to every Application and Project, with sections for overview, I/O, tags, control logic, screens and alarms. The IDE keeps its tag and Modbus tables up to date; the AI Designer reads it. See Projects and Applications.

1.6.1 I/O acronyms used in this manual

Acronym Meaning Example
DI Digital input — on/off signal into the controller float switch, push button, limit switch
DO (or DQ) Digital output — on/off signal out of the controller contactor coil, relay, indicator lamp
AI Analog input — continuously variable signal in 4–20 mA pressure transmitter, 0–10 V level sensor
AO (or AQ) Analog output — continuously variable signal out 4–20 mA valve command, 0–10 V speed reference

The IDE assigns the %I / %Q / %IW / %QW addresses when you add a device; you never type them by hand.

1.6.2 Arena memory

The arena is the controller’s shared in-memory tag store: one block of RAM holding the current value of every tag. The ladder runtime writes it every scan; the HMI, the Modbus server, the Python task, the historian and the cloud agent all read and write the same block, which is why every part of the controller sees the same values. Day to day you can think of it as “where tag values live”. The name shows up in Tag Monitor, in ArenaTCP (one controller or PC reading another controller’s arena) and in the Python ctx.read_tag() call.

1.7 A tour of the workspace

Annotated workspace with all panels labelled
  1. Toolbar — file operations, Build, Build & Emulate, Connect, the controller’s run/stop/debug modes and monitoring. The row changes with the active project’s Target.
  2. Project tree — the open Application and everything in its Projects. In the Cloud edition a Cloud tab beside it is the Cloud Explorer.
  3. Editor area — the Tag Database, ladder programs, HMI screens and other editors, each in a tab.
  4. Properties panel — click anything (a tag, a ladder element, a widget, the project itself) and edit it here. This is how all editing of existing items works; dialogs are only for creating and deleting.
  5. Toolbox — what you can add to the active editor.
  6. Bottom panel — Output (build and deploy messages), Statistics, Tag Monitor (live values), Python Log, and the others described in The Workspace.
  7. Status bar — project name, cloud sign-in state and controller connection state.

The Workspace covers every region in detail.

1.8 Three ways to run a program

Before any build, the IDE checks that every tag your logic refers to exists in the Tag Database and offers to create the missing ones.

1.9 What’s next

The Quick Start walks you through one physical scenario — a motor, three float switches, an Off/Hand/Auto selector and a latched high-level alarm — three times over:

If you have a controller on your bench, start with The Walkthrough scenario. If you do not, the ladder and Python walkthroughs can be completed against the emulator with Build & Emulate; only the deploy and the physical test need hardware.

2 The Walkthrough scenario

The next three chapters walk you through three end-to-end projects that share one physical setup. Building the same control system three different ways — once in ladder logic, once in Python, once as a separate HMI panel on a PC — is the fastest way to see what each editor in the Whisker IDE actually does and how the pieces fit together. The shared scenario also gives you something concrete to keep on your bench: at the end of the three walkthroughs, you’ll have one running controller and one running HMI panel that you can exercise by toggling float switches and watching the system respond.

This chapter sets up everything the walkthroughs assume — the hardware, the field wiring, the tag names, the control intent, and the HMI layout. Read it once. Each walkthrough then refers back here for setup details rather than repeating them.

2.1 What you’re building

A single motor controlled by three float switches and an operator selector. The operator chooses one of three modes — Off, Hand, or Auto — using an AHO selector switch on the HMI. In Auto mode, the motor runs when the start float closes and stops when the stop float opens, like a classic seal-in. In Hand mode, the motor runs unconditionally; the operator owns it. In Off mode, the motor is off.

A separate high-alarm float acts as a safety. When it closes, the controller latches an alarm and refuses to run the motor in Auto until the operator presses a Clear Alarms button on the HMI. In Hand mode the operator can still run the motor through the alarm — that lets them pump down or service the system without fighting the alarm latch. Off still means off, alarm or no alarm.

That’s the whole scenario. Four inputs (three floats plus an operator mode selection), one output, one latched alarm, one operator panel.

System diagram — tank, three floats, pump, mSmart Universal IO, Nexus.io controller

The diagram above shows the physical setup. The three float switches (Start, Stop, High) sit at three heights inside the tank. The pump drives the tank’s discharge. All four field signals wire into the mSmart Universal IO module on inputs DI1, DI2, DI3 and output DO1 — those module channels become the tags float_start, float_stop, float_alarm, and motor_run after you rename them in Step 3 of each walkthrough. The Nexus.io Automation Controller talks to the mSmart module over Modbus RTU on its RS-485 port; the wiring map below spells out which DI/DO maps to which tag.

2.2 Choose your path

A new Nexus.io controller arrives ready to use: plug it into your LAN and the IDE finds it as a Standalone controller that accepts a plain connection and takes whatever program you build. How you work with it from there depends on one question — is this controller going to be connected to the Whisker.io cloud? Every walkthrough step that depends on the answer is marked Path A or Path B. Everything else — tags, logic, screens, testing — is the same.

Path A — Cloud. You use the Cloud edition of the IDE and have a Whisker.io account. In the Cloud Explorer you provision the controller as a device in your account; the IDE hands it its enrolment bootstrap and the controller fetches its own certificate from your account’s certificate authority, then restarts in managed mode. From then on the IDE connects to it securely with the certificate the cloud issued to you at login, only signed programs from your account are accepted, and its data flows to your account. A cloud-connected controller is always managed — enrolment does that for you. See Security, Provisioning and Enrolment for the full story.

Path B — No cloud. You run the IDE without an account. Connect to the controller by discovery or by address and deploy; nothing else is required. Whether the controller should also be managed — every engineer with their own identity, only signed programs accepted, revocation when someone leaves — is your call. If you want that without a cloud account, one PC at your site becomes the site’s certificate authority (Security dialog → Site section → Make this keystore a site…) and provisions the controller with Provision unit (site); engineers import an identity package from that PC. You own that certificate authority; D6 keeps no copy of it. See Security, Provisioning and Enrolment. None of the walkthroughs needs it.

Tip. If you have an account, do Path A’s provisioning before Step 8 of the first walkthrough — it is one dialog. On Path B there is nothing to do in advance. {.tip}

Steps 1–7 (creating the project, tags, logic and screen) need no controller at all — and if you have no controller yet, Build & Emulate runs the finished project on your PC: the IO Stimulus tab lets you flip the float switches by hand and the HMI Preview tab shows the screen (see Connecting, Deploying and Emulating).

2.3 The hardware

You need three things:

If you don’t have a real motor handy, a 24 V relay or a single LED on the DO will do — you just need to see something react when the logic turns the output on.

2.3.1 Wiring map

Field signal Module channel Modbus register Tag name (after rename)
Start float DI1 Holding reg 0 float_start
Stop float DI2 Holding reg 1 float_stop
High-alarm float DI3 Holding reg 2 float_alarm
Motor contactor DO1 Holding reg 99 motor_run

The mSmart Universal IO module exposes a lot more than this — six DIs, five AIs, an AO, plus counters and event-rate readbacks — but the walkthroughs only use four channels. Anything you don’t reference in the project stays unmapped and ignored.

2.3.2 RS-485 settings

The demo mSmart module talks Modbus RTU at the following settings. Both ends of the RS-485 link must agree, or no data flows.

Setting Value
Baud rate 9600
Data bits 8
Parity None
Stop bits 1
Slave ID 1
Controller serial port rs485 (UART 1)

Set the baud rate to 9600 on the controller. The demo module runs at 9600, not the 19200 some controllers ship with — if the rate is wrong you’ll add the device fine but read all zeros. The controller’s RS-485 settings are described in Setting Up Your Nexus.io Automation Controller. The IDE-side device picks up its rate from the same rs485 port, so the two stay in sync once the controller is set.

2.4 Tags — where they come from

The walkthroughs use the twelve tags below — four I/O tags and eight memory tags. (The Python walkthrough skips const_hand, so it uses eleven.) They come from two different places in the IDE, and getting the order right matters:

So the setup sequence in each walkthrough is:

  1. Add the mSmart Universal IO module in the IO Configuration editor.
  2. The IDE auto-creates one tag per channel on the module (uio_DI1, uio_DI2, …, uio_DO1, …). Rename the four you care about to friendlier names.
  3. Open the Tag Database and add the eight memory tags that have no physical counterpart.
Name Type Address Origin What it is
float_start BOOL %I0 auto from uio device, renamed from uio_DI1 Start float input
float_stop BOOL %I1 auto from uio device, renamed from uio_DI2 Stop float input
float_alarm BOOL %I2 auto from uio device, renamed from uio_DI3 High-alarm float input
motor_run BOOL %Q0 auto from uio device, renamed from uio_DO1 Motor output
aho_mode INT %MW0 added in Tag Database Operator mode: 0=Off, 1=Hand, 2=Auto
const_hand INT %MW1 added in Tag Database (initial value 1) Constant 1, used as IN2 of the CMP block. The CMP inputs require tag references — this is how you spell the constant “Hand”
motor_off BOOL %MX0 added in Tag Database True when aho_mode = 0; set by the CMP block
motor_hand BOOL %MX1 added in Tag Database True when aho_mode = 1; set by the CMP block
motor_auto BOOL %MX2 added in Tag Database True when aho_mode = 2; set by the CMP block
call_motor BOOL %MX3 added in Tag Database “Tank needs the motor” — set by start float, reset by stop float
alarm_latched BOOL %MX4 added in Tag Database High-alarm latch (set by start-of-alarm float, cleared by button)
clear_alarms BOOL %MX5 added in Tag Database Clear-alarms button (one-shot, set by HMI, cleared by logic)

A few conventions to know:

The Whisker IDE uses these IEC-61131 address conventions for all PLC-style projects. The full address map is in the Tag Database chapter of the full manual.

The other auto-created tags. When you add the mSmart Universal IO module the IDE creates one tag for every channel on the device — roughly 30 tags. You only rename four. The rest stay in your project as untouched uio_* tags. They don’t hurt anything; the I/O scanner reads them every scan whether you use them or not. If you want a tidier Tag Database, the IO Configuration editor lets you remove individual unused points.

2.5 The control logic — what it does

All three walkthroughs implement the same behavior. Reading this specification before diving into a walkthrough makes the editors a lot easier to follow.

The logic factors into five small steps. Each step is one ladder rung in the Ladder Walkthrough and a matching few lines of app.py in the Python Walkthrough:

(1) Mode decode — one CMP block compares aho_mode against const_hand (=1):
        motor_off  := aho_mode  < const_hand    # LT output  (aho_mode = 0 → Off)
        motor_hand := aho_mode == const_hand    # EQ output  (aho_mode = 1 → Hand)
        motor_auto := aho_mode  > const_hand    # GT output  (aho_mode = 2 → Auto)

(2) Alarm latch:
        if float_alarm:  alarm_latched := TRUE

(3) Clear alarms (one-shot):
        if clear_alarms:
            alarm_latched := FALSE
            clear_alarms  := FALSE     # auto-release the button

(4) call_motor latch (Auto-mode seal-in):
        if float_start:  call_motor := TRUE      # tank wants the motor
        if float_stop:   call_motor := FALSE     # tank doesn't anymore

(5) Motor output:
        motor_run := motor_hand
                  OR (motor_auto AND call_motor AND NOT alarm_latched)

A few points worth flagging because they catch people out:

2.6 The alarm

A single alert definition in each project:

Field Value
Name HighLevelAlarm
Condition float_alarm = TRUE
Latched No (unchecked)
Output tag (blank — leave it empty)

Name, condition, latch flag, and output tag are the only configurable fields — there is no separate severity or message.

The alert is deliberately non-latched. Each time float_alarm goes TRUE it records a FIRED entry in the alert history, and each time it goes FALSE it records a CLEARED — so every occurrence shows up, not just the first. (A latched alert fires once and stays active until an explicit reset, which would give you a single stuck entry instead of an event log.)

Latching lives in the ladder, not the alert. The motor interlock is held by the Set/Reset coils that drive alarm_latched (the control logic above), and the Clear Alarms button clears that latch. The alert sits alongside it purely to record occurrences for the history and the HMI alert table.

2.7 The HMI screen

One screen, identical across the three walkthroughs. Walkthrough 3’s WhiskerHMI screen is literally a copy of the Walkthrough 1 embedded HMI screen with the controller IP filled in.

Five widget types do all the work:

Sketch of the HMI screen with widgets labeled

2.8 Watching the live data — Tag Monitor

Before you start a walkthrough, it helps to know where you’ll watch the controller think. The Whisker IDE has three live-data views, and you’ll lean on all of them during testing:

If you only remember one: the Tag Monitor is the one you’ll open first when something doesn’t behave the way you expect.

Note. Live values appear only after the IDE knows the controller is running the project you have open — in practice, after you press Build in Step 8 of a walkthrough. Until then the Tag Monitor stays empty on purpose, so you never read a stale program’s memory under your project’s tag names.

2.9 What’s next

You’re ready to build. Pick a walkthrough:

The walkthroughs are independent. You don’t have to do all three, and you can do them in any order — though Walkthrough 3 assumes the controller from Walkthrough 1 or 2 is already running so it has something to connect to.

3 Your First PLC Project — Ladder

This walkthrough builds the scenario from The Walkthrough scenario as a ladder-logic PLC project running on a Nexus.io Automation Controller. By the end you’ll have a working motor controller with a latched alarm, an Auto/Hand/Off selector, and a touchscreen HMI on the controller’s display.

Allow about 45 minutes the first time through. Each subsequent walkthrough will go faster because you’ll already know the IDE’s mechanics.

3.1 Before you start

You need:

3.2 Step 1 — Create the Application and Project

  1. Launch the Whisker IDE.

  2. From the toolbar, click the New Application icon (the leftmost icon, a small workspace symbol).

    New Application toolbar icon highlighted

  3. The New Application dialog appears. Fill it in:

    • Application name: Walkthrough-Ladder
    • Description: leave blank (or type something brief like “Motor control demo”)
  4. Click New Project. A New Project sub-dialog opens. Fill it in:

    • Project name: Motor
    • Target hardware: Nexus.io AC

    Click Add to attach the Project to the new Application — you’ll return to the outer Application dialog with Motor listed.

  5. Back in the outer Application dialog, click Create.

    New Project dialog with Motor / Nexus.io AC selected

  6. The IDE automatically opens a Save dialog for the new Application. Pick somewhere convenient (your Documents folder is fine) and accept the filename Walkthrough-Ladder.widez. No need to press Ctrl+S — the Create button saves for you.

When the dialog closes you should see:

The Motor project comes pre-populated with a Main task and a Main ladder program — that’s the file you’ll write the logic in. We’ll get there in a few steps.

3.3 Step 2 — Add the mSmart Universal IO module

  1. In the Project Tree, click Motor → Setup → IO Config. The IO Configuration editor opens.

  2. Click Add Device in the toolbar. The Add Device dialog opens.

  3. Configure the device:

    • Device Kind: select Modbus / EtherNet-IP device (from IODF). This is the first choice in the dialog and it determines which fields appear below.
    • Device Type: pick mSmart-Universal-IO from the dropdown. (If it’s not there, the IDE hasn’t seeded its default IODFs yet — re-open this dialog and it should appear.)
    • Instance name: uio (short for “universal IO” — you’ll reference this name later when mapping points).
    • Connection settings auto-fill from the device type:
      • Port: rs485 (the RS-485 port on the Nexus.io controller)
      • Slave ID: 1

    Add Device dialog with mSmart-Universal-IO selected

  4. Click Add Device. The new device appears in the Configured Devices panel with a cable-style RTU icon to its left.

  5. Look at the Digital Inputs, Digital Outputs, Analog Inputs, and Analog Outputs panels below the Configured Devices panel. The IDE has auto-created roughly 30 tags — one for every channel on the module — and distributed them into the right panel by type. Each tag is named uio_<channel> (so uio_DI1, uio_DI2, …, uio_DO1, …) and has an auto-assigned IEC address.

    IO point panels auto-populated from the device

  6. Disable the IO points you won’t use. Each row in the Digital Inputs / Digital Outputs / Analog Inputs / Analog Outputs panels has a checkbox at its left edge. When enabled (the default) the IO scanner polls that channel every scan cycle. The walkthrough uses only four channels — DI1, DI2, DI3, and DO1. Enable those and disable everything else:

    • Digital Inputs: keep DI1, DI2, DI3; uncheck DI4, DI5, DI6.
    • Digital Outputs: keep DO1 (it’s the only one).
    • Analog Inputs: uncheck all of them — AI1–AI5, every … Filter Window, every … Total Count, every … Events Per Min, and Solar Voltage. (The per-channel counters and filter windows are word values, so the IDE files them under Analog Inputs, not Digital Inputs.)
    • Analog Outputs: uncheck AO1 (uio_AO1).

    Disabled rows dim out and stay in your project — flip the checkbox back on if you want them later.

    Why this matters. The IO scanner does one Modbus transaction per enabled point per scan cycle. With all ~30 points enabled you’re spending most of the bus’s time reading values you never look at, and any one of those transactions can time out and slow the next scan. Trimming to the four you use takes wire traffic from ~30 transactions/scan down to ~2 (one coalesced read of DI1-3 plus one write to DO1) and makes the end-to-end response feel instant.

  7. Press Ctrl+S.

Why this is the right place to create I/O tags. Tags that map to physical I/O don’t make sense without a module behind them — the address %I0 only exists because there’s an input channel on a device somewhere. The IDE enforces this: you can’t type a %I or %Q address into the Tag Database directly. You add the device, the IDE creates the tags, you rename them.

3.4 Step 3 — Rename the I/O tags you’ll use

The 30 auto-created tags include analog readings, pulse counters, event rates, and config registers. The walkthrough only uses four of them. Renaming those four to friendlier names makes the ladder and HMI easier to read.

  1. In the Project Tree, expand Motor → Tags and click Tag Database. The Tag Database editor opens. You’ll see the uio_* tags the IO module created.

  2. Double-click the uio_DI1 row’s name cell to edit it in place. Type float_start and press Enter. There is no “Rename” button or menu — name editing is just an in-place edit on the name cell.

  3. Repeat for the other three:

    From To Why
    uio_DI1 float_start Start float input
    uio_DI2 float_stop Stop float input
    uio_DI3 float_alarm High-alarm float input
    uio_DO1 motor_run Motor contactor output

    The addresses (%I0, %I1, %I2, %Q0) don’t change — only the names do. Anywhere else in the project that referenced the old names is updated automatically.

    Tag database after the four renames

  4. Press Ctrl+S.

3.5 Step 4 — Add the memory tags

Eight more tags don’t correspond to any physical wire — they’re internal state the logic uses to remember things between scans, plus the mode flags the CMP block in Step 5 will produce. All of them go in the Tag Database directly.

  1. Still in the Tag Database editor, click Add Tag.

  2. First tag — the operator mode:

    • Name: aho_mode
    • Type: INT
    • Class: VAR
    • Memory Area: Memory Word (16-bit) → address auto-fills %MW0
    • Description: Operator mode (0=Off, 1=Hand, 2=Auto)

    Click Add.

  3. Repeat for the remaining seven:

    Name Type Class Memory Area Address (auto) Initial value Description
    const_hand INT CONST Memory Word (16-bit) %MW1 1 Constant used as IN2 of the CMP block — the AHO “Hand” value
    motor_off BOOL VAR Memory Bit %MX0 Mode-decode output: TRUE when aho_mode = 0
    motor_hand BOOL VAR Memory Bit %MX1 Mode-decode output: TRUE when aho_mode = 1
    motor_auto BOOL VAR Memory Bit %MX2 Mode-decode output: TRUE when aho_mode = 2
    call_motor BOOL VAR Memory Bit %MX3 Auto-mode seal-in: set by start float, reset by stop float
    alarm_latched BOOL VAR Memory Bit %MX4 Latched high-alarm
    clear_alarms BOOL VAR Memory Bit %MX5 Operator clear-alarms pulse

    Set the Class column carefully. Every tag here is VAR (the default, like aho_mode) except const_hand, which must be CONST. The Class field is in the Tag properties panel.

    Why a tag for a constant? The CMP block’s IN1 and IN2 inputs both expect tag references — the IDE doesn’t let you type a literal number into IN2. So we make one tag, const_hand, with Class CONST and an initial value of 1, and use it as the comparison operand. As a CONST it never changes at runtime; it’s just how you spell “the integer 1” to the CMP block. (Leaving it VAR lets the logic overwrite it, which breaks the comparison.)

    Tag database with all memory tags added

  4. Press Ctrl+S.

About address prefixes. %I and %Q were assigned to your I/O tags by the module. %MX is the prefix for internal memory bits, %MW for 16-bit words, %MD for 32-bit double-words, and %MF for 32-bit floats. The Memory Area dropdown lets you pick the right one; the IDE fills the address number automatically based on which slots are already taken.

3.6 Step 5 — Write the ladder logic

In the Project Tree, expand Motor → Tasks and click Main. (Main is a task, not a folder, so it doesn’t expand — a single click on the task opens its ladder program directly; a double-click works too.) The Ladder Editor opens with five empty networks — what other PLC platforms call rungs. The sub-steps below add more networks as you need them using the + button at the top of the editor.

One coil per network. The Whisker IDE enforces a single terminal (coil, timer, counter, FB, etc.) per network. If two outputs need to be driven from the same condition — for example, “when clear_alarms is pressed, reset alarm_latched AND reset clear_alarms” — you spell that as two networks that share the same contact pattern, not as two parallel branches inside one network. That’s why the walkthrough uses seven networks rather than five.

Ladder editor shortcuts you’ll use in this step. The IDE leans on keyboard shortcuts more than drag-and-drop for branch wiring. Worth learning early:

Shortcut What it does
drag from Toolbox drop an instruction onto the selected segment
Ctrl + Down create a parallel branch below the current branch
Ctrl + Up reconnect the current branch back up to its parent
Ctrl + Right extend a wire to the right on the current branch
Del delete the selected instruction
Ctrl + Del delete the entire current network
+ (toolbar) add a new empty network at the bottom

Two conventions for the networks below. (1) You don’t need to click a network to “select” it first — just drag the instruction onto the target network’s segment. (2) In the element dialogs a contact asks for a Tag Name and a coil asks for an Output Tag; the steps use those labels.

3.6.1 Network 1 — Decode the operator mode (CMP)

The IDE has single comparison contacts (EQ, NE, GT and so on — see the Ladder Instruction Reference), but it also ships a CMP function block that performs one comparison and writes three BOOL output tags directly — one each for “less than”, “equal”, and “greater than”. That’s exactly what we want for AHO — one network gives us motor_off, motor_hand, motor_auto all at once.

CMP is a sink instruction (like a coil). It has no right-hand output pin; instead its three outputs are set as properties in the property dialog and written to tag values directly. You drop one CMP in the terminal slot of a network and the network is complete.

  1. From the Toolbox, drag a CMP block (under Compare) onto the terminal slot at the right end of network 1. A dialog appears. Give the block an Instance name (e.g. cmp_mode), then set the properties in the order the dialog lists them — IN1, IN2, then GT, EQ, LT:

    • IN1: aho_mode
    • IN2: const_hand (the constant tag you added in Step 4 — the CMP block expects a tag reference here, not a literal number)
    • GT output: motor_auto (Rationale: aho_mode > 1 means aho_mode = 2, which is Auto.)
    • EQ output: motor_hand (Rationale: aho_mode = 1 is Hand.)
    • LT output: motor_off (Rationale: aho_mode < 1 means aho_mode = 0, which is Off.)

    Click OK. The CMP block sits in the terminal slot with the four bound tag names labelled on it. There is no coil to wire — the CMP block writes the three output tags itself every scan.

    Network 1 — CMP block decoding AHO mode

What’s nice about this pattern. Three BOOL flags fall out of one CMP network. The rest of the program reads them like any other contact; nobody downstream needs to know aho_mode is an integer or what its values mean. That separation makes it easy to add a fourth mode later (e.g. a Maintenance mode at aho_mode = 3) — add a second CMP to compare aho_mode to the value 2 and you have four mode flags from two networks.

3.6.2 Network 2 — Latch the alarm

When the high-alarm float closes, latch alarm_latched with a Set coil so the bit stays on after the float opens.

  1. Click into network 2.

  2. Drag a Normally Open Contact onto the first segment. Tag: float_alarm.

  3. Drag a Set Coil ((S)) — the coil marked with an s in the toolbox — to the terminal slot. There is no “mode” to choose: the popup asks only for the output tag. Enter alarm_latched.

    Network 2 — alarm latch

3.6.3 Network 3 — Clear the alarm latch

When the operator presses Clear Alarms, reset alarm_latched.

  1. Click into network 3.

  2. Drag a Normally Open Contact onto the first segment. Tag: clear_alarms.

  3. Drag a Reset Coil ((R)) to the terminal slot. Tag: alarm_latched.

    Network 3 — reset alarm latch

3.6.4 Network 4 — Auto-release the Clear Alarms button (one-shot)

clear_alarms is driven by the HMI button. The HMI writes TRUE while the button is pressed; we want the controller to immediately write it back to FALSE so the next scan sees a fresh edge if the operator presses again. This network resets clear_alarms using its own value as the trigger — that’s what makes the button behave as a one-shot pulse even though the HMI widget writes a level.

  1. Click into network 4.

  2. Drag a Normally Open Contact onto the first segment. Tag: clear_alarms.

  3. Drag a Reset Coil ((R)) to the terminal slot. Tag: clear_alarms.

    Network 4 — clear_alarms one-shot

Why two networks instead of one? Networks 3 and 4 fire from the same condition (clear_alarms = TRUE) but drive different coils, and the IDE allows only one coil per network. So we spell the “reset both” intent as two networks with identical contact patterns. They execute in the same scan, so the behavior is equivalent to “reset both at once”.

3.6.5 Network 5 — call_motor SET (start float closes the tank’s request)

Pulling the Auto-mode seal-in out into its own pair of networks keeps the motor output readable. call_motor represents “the tank is asking for the motor to run” — it goes TRUE when the start float closes and stays TRUE (latched) until the stop float clears it in the next network.

  1. Click into network 5.

  2. Drag a Normally Open Contact onto the first segment. Tag: float_start.

  3. Drag a Set Coil ((S)) to the terminal slot. Tag: call_motor.

    Network 5 — call_motor set

3.6.6 Network 6 — call_motor RESET (stop float clears the request)

  1. Click the ‘+’ button to add a new network - creates network 6.

  2. Drag a Normally Open Contact onto the first segment. Tag: float_stop.

  3. Drag a Reset Coil ((R)) to the terminal slot. Tag: call_motor.

    Network 6 — call_motor reset

3.6.7 Network 7 — Drive motor_run

The payoff network. With all the state already decoded above, this one reads like a one-line sentence:

“Motor runs in Hand, or in Auto when the tank is calling and the alarm isn’t latched.”

Two parallel branches feed one Simple coil:

  1. Click the + button to add network 7.

  2. Drag a Normally Open Contact onto the first segment. Tag Name: motor_hand.

  3. Drag a Simple Output coil to the terminal slot (that’s the label in the toolbox tooltip). Output Tag: motor_run.

  4. With the motor_hand contact selected, press Ctrl + Down to add a parallel branch below.

  5. On the new branch, drag in series:

    • Normally Open Contact, tag motor_auto.
    • Normally Open Contact, tag call_motor.
    • Normally Closed Contact, tag alarm_latched.

    With the last contact (alarm_latched) selected, press Ctrl + Up to reconnect the sub-branch back to the parent rung so the two branches feed the same motor_run coil. (The branch does not auto-reconnect; you have to ask for it explicitly.)

    Network 7 — motor_run output

  6. Press Ctrl+S.

Tip: read the network out loud. “Motor runs when (Hand) or (Auto AND call_motor AND no alarm).” If that sentence matches the behavior you want, the network is correct. If you ever need to change which conditions inhibit the motor, this is the only network you’ll touch — the seal-in (networks 5–6) and mode decode (network 1) stay as-is.

3.7 Step 6 — Define the HighLevelAlarm alert

  1. In the Project Tree, click Motor → Alerts. The Alerts editor opens.

  2. Click Add Alert. The dialog asks only for a Name — enter HighLevelAlarm and confirm. The new alert appears in the Alerts list; select it and configure the rest in the Properties panel on the right:

    • Condition: build float_alarm == True. Pick float_alarm from the left dropdown; because it’s a BOOL tag the value field becomes a True / False picker — choose == and True. (The condition editor adapts to the tag’s type: BOOL gives a True/False picker, INT/DINT a whole-number field, and REAL a decimal field.)
    • Latched: leave unchecked. A non-latched alert tracks the condition — it logs a FIRED each time float_alarm goes true and a CLEARED when it goes false, so every occurrence shows up in the alarm history. (A latched alert fires once and stays active until an explicit reset — not what you want for an event log.)
    • Output tag: leave blank. The motor interlock is latched separately by the ladder Set/Reset coils in Networks 2–3; this alert just reports occurrences for the history and alert table.

    The Alert system does not currently have severity or per-alert message fields; the only configurable pieces are name, condition, latch flag, and output tag.

    Alert editor with HighLevelAlarm configured

  3. Press Ctrl+S.

Latching lives in the ladder, not the alert. The motor interlock latches via the Set/Reset coils in Networks 2–3 (alarm_latched), and the Clear Alarms button clears it. The alert is deliberately non-latched so the alarm history logs every high/low of the float instead of one stuck entry. Two separate jobs: the ladder holds the interlock; the alert records events.

3.8 Step 7 — Build the HMI screen

  1. In the Project Tree, click Motor → HMI Screens. Click Add Screen in the toolbar and name it Main. The HMI Designer opens with an empty canvas.

  2. Place the widgets on the canvas. Widgets aren’t dragged in from the Toolbox — single-click a widget in the Toolbox and the IDE autoplaces it on the canvas; then drag it from the canvas into the position you want. Place these widgets:

    • selectorSwitch at the top — for AHO mode.
    • pump below it — for motor run status.
    • text above the pump — label for the motor.
    • led × 3 in a row below the pump — for the three float inputs.
    • pushButton at the bottom right — for Clear Alarms.
    • alertTable along the bottom — for the alarm display.
  3. Configure each widget by clicking it and editing the Properties panel.

    Tag bindings live at the bottom of the Properties panel. For each widget below, click it on the canvas and scroll the Properties panel to its Bindings section at the bottom. That’s where every “Bind X → tag” instruction below is set; the upper parts of the Properties panel cover label, colors, and other look-and-feel options.

    • selectorSwitch (the properties appear in this top-to-bottom order in the panel):

      • Positions: 3
      • Labels: OFF, HAND, AUTO
      • Bind position → aho_mode (in the Bindings section at the bottom of the panel)
    • pump (motor run):

      • Bind Running → motor_run. The pump has no single “state” binding — it exposes separate Running and Faulted bindings.
      • Faulted: leave unbound — this walkthrough has no motor-fault tag.
      • Active color: green (shown while Running is true).
      • Fault color: red (shown when Faulted is true). It never shows here since Faulted is unbound, but in a real project a motor overload bit would bind to Faulted and operators rely on the red/green distinction for quick “is something wrong?” reads.
      • Body color: dark gray (the static housing of the pump icon).
    • text (motor label):

      • Text: Motor (the text widget’s field is Text, not Label)
    • led × 3 (floats): bind each one to float_start, float_stop, float_alarm respectively. Labels: Start Float, Stop Float, High Alarm.

    • pushButton:

      • Bind output → clear_alarms
      • Mode: momentary
      • Label: Clear Alarms
    • alertTable: no binding needed; just drop it on the canvas. It reads alarm state from the alert system on its own.

    Completed HMI screen in the Designer

  4. Press Ctrl+S.

3.9 Step 8 — Connect and Build

  1. Click the Connect icon in the toolbar (the plug icon to the right of Build). The Connect dialog opens and starts scanning the LAN. Each controller it finds is listed with its posture: a new controller shows as Standalone; one you have provisioned into your account shows as Managed.

    Awaiting enrolment? That posture only appears on a controller ordered with management required; it accepts nothing until it is provisioned (Path A, or the site CA). See Security, Provisioning and Enrolment.

  2. When your controller appears in the list, click it and click Connect. If it does not appear — mDNS discovery is blocked on many corporate networks and never crosses a VPN — click Enter address manually and type the controller’s IP address.

    • Path A: the controller is managed, so the IDE connects securely using the certificate your account issued to you when you logged in. Nothing to configure.
    • Path B: the IDE connects plainly. (If you have made the controller managed through a site CA, it connects with your site identity instead; with more than one site imported, pick the right one under Project Properties → Security → Site identity first.)
  3. A confirmation notification appears at the bottom of the IDE and the status bar shows the connection. The first time you connect with this project the IDE also compares it with whatever the controller already holds. A brand-new controller holds nothing, so nothing happens; if you later connect to a controller that runs a different project, a dialog offers to Download from target — choose it only if you want to edit that project instead of yours.

  4. Press F5 (or click Build). Build is only enabled while you are connected, because for a Nexus.io project one press does both jobs: the IDE compiles, then downloads the program to the connected controller (signing it first when the controller is managed). The Output panel shows the sequence:

    Building project: Motor...
    Build succeeded: 12 files generated
    Uploading program to target...
    Reloading program...
    Starting VM...

    On a managed controller (Path A) you will also see Signed by … and Uploading signed bundle to target....

    If the build stops with a list of undefined tags, a name you typed somewhere (a contact, a coil, a widget binding) does not exist in the Tag Database. The dialog offers to create them; check the types it proposes and try again.

  5. The controller loads the new program and starts it; the touchscreen switches to your Main screen within a few seconds.

    Output panel showing a successful build and download

Note. From this point on the IDE knows the controller is running exactly this project, and the Tag Monitor and Debug Mode come alive. If you edit the project afterwards, they switch off again until your next Build — that is deliberate, so you never watch live values under tag names the running program does not have.

3.10 Step 9 — Test it

If your hardware is wired and your float switches are accessible, this is the fun part.

Open the Tag Monitor first. Click the Tag Monitor tab in the bottom panel. With the controller connected you’ll see live values for every addressed tag. Type float in the filter box to keep float_start, float_stop, float_alarm (and motor_run) on screen. As you exercise each switch in the steps below, watch these values flip in real time — if a switch doesn’t move its tag, the issue is wiring or slave config, not your ladder logic.

  1. Verify the HMI — Walk over to the controller’s touchscreen. The Main screen should be showing the layout you designed. The mode selector starts at OFF, all LEDs are dark.

  2. Hand mode — Tap HAND on the selector. The Motor LED turns green and DO1 energizes on the I/O module. Confirm by looking at the module’s DO1 LED.

  3. Off mode — Tap OFF. Motor LED goes dark, DO1 de-energizes.

  4. Auto mode without floats — Tap AUTO. With both floats open, motor stays off (no start signal yet).

  5. Auto seal-in — Briefly close the start float. The Motor LED should turn on and stay on after the float opens again.

  6. Auto stop — Close the stop float. Motor LED goes off.

  7. High alarm — Close the high-alarm float. The Alarm LED lights, the alert table shows HighLevelAlarm, and the motor turns off (if it was on in Auto). Now open the float again: the LED and alert table clear (they track the live float), but the motor stays off — the ladder’s Set-coil latch (Network 2) holds the interlock until you clear it. Each high/low of the float adds a FIRED/CLEARED pair to the alarm history.

  8. Hand override — While alarmed, tap HAND. Motor turns back on (the safety override). Tap back to AUTO — motor turns off again.

  9. Clear the interlock — Tap Clear Alarms. This resets the ladder’s alarm_latched (Network 3), releasing the motor interlock so the motor can run again in Auto. (The alert table already cleared when you opened the float — it tracks the live condition.)

If all nine steps behave as described, you’re done with the Ladder walkthrough. The controller is now running a complete motor-control program with an alarm system and an operator panel.

3.11 What’s next

4 Your First PLC Project — Python

This walkthrough builds the same scenario as the Ladder walkthrough, on the same Nexus.io controller, with the same I/O module and the same HMI screen — but the control logic lives in app.py instead of in ladder networks (rungs). If you’ve already finished the Ladder walkthrough, most of the setup steps will be familiar.

Reading both walkthroughs side by side is the fastest way to understand when ladder is the right tool and when Python is. Short version: ladder is unmatched for boolean interlock logic the way a maintenance electrician will read it; Python is unmatched when you need arithmetic, data structures, conditionals more than two levels deep, or a library call. The motor scenario fits both, which is exactly why it’s a useful comparison.

Allow about 30 minutes if you’ve already done the Ladder walkthrough, or 45 minutes if this is your first.

4.1 Before you start

Same prerequisites as the Ladder walkthrough — a Nexus.io controller on your LAN (provisioned into your account first on Path A), the mSmart Universal IO wired up, and the Walkthrough scenario chapter read.

4.2 Step 1 — Create the Application and Project

Same procedure as Walkthrough 1, Step 1. The only difference is the naming: call the Application Walkthrough-Py and the Project Motor (target Nexus.io AC). Save as Walkthrough-Py.widez. We use a different filename so it can coexist with the Ladder walkthrough project on your disk.

(If you skipped Walkthrough 1, see its Step 1 for screenshots of the dialogs.)

4.3 Step 2 — Add the mSmart Universal IO module

Same as Walkthrough 1 Step 2 — open Motor → Setup → IO Config, click Add Device, set Device Kind to Modbus / EtherNet-IP device (from IODF), pick mSmart-Universal-IO as the Device Type, instance name uio, port rs485, slave ID 1, defaults for the rest. The IDE auto-creates ~30 uio_* tags, one per channel on the module.

Don’t forget the Disable the IO points you won’t use sub-step from the Ladder Walkthrough Step 2 — uncheck everything except DI1, DI2, DI3, and DO1. Same reasoning as the ladder walkthrough: the IO scanner does one Modbus transaction per enabled point per scan, so trimming to the four channels you actually read makes the controller’s response to a float change feel instant.

4.4 Step 3 — Rename the I/O tags

Same as Walkthrough 1 Step 3 — in the Tag Database, rename the four tags the walkthrough uses:

From To
uio_DI1 float_start
uio_DI2 float_stop
uio_DI3 float_alarm
uio_DO1 motor_run

The tag names are what the Python code references — that’s why they have to match. The addresses (%I0/%I1/%I2/%Q0) are unaffected by the rename.

4.5 Step 4 — Add the memory tags

Add the seven memory tags below — the same set as Walkthrough 1 minus const_hand. The ladder version needed const_hand only because the CMP block can’t take a literal operand; in Python you compare aho_mode == 1 directly, so there’s no constant tag to create. The Python code in Step 5 still writes the four intermediate tags (motor_off / motor_hand / motor_auto / call_motor) just like the ladder version does, so you can see the same intent expressed two ways.

Name Type Memory Area Address
aho_mode INT Memory Word (16-bit) %MW0
motor_off BOOL Memory Bit %MX0
motor_hand BOOL Memory Bit %MX1
motor_auto BOOL Memory Bit %MX2
call_motor BOOL Memory Bit %MX3
alarm_latched BOOL Memory Bit %MX4
clear_alarms BOOL Memory Bit %MX5

4.6 Step 5 — Write the Python application

This is the part that differs. The Motor project comes with a default app.py containing a stub run(ctx) function and a brief comment explaining the available API. You’ll replace the stub with the motor control logic.

  1. In the Project Tree, expand Motor → Python and click app.py. The Python Editor opens (a single click is enough; there’s no separate “open” step).

    Editor tips. Tab and Shift-Tab indent/outdent the currently-selected lines (no selection = insert 4 spaces / outdent the current line). Pasting code from another editor, chat, or the web normalizes tab characters to 4 spaces automatically — no need to reformat first.

  2. Replace the entire contents of the file with the script below.

    Don’t retype it — and don’t copy it out of this PDF. Copying Python from a PDF mangles the indentation (and PDFs may split the block across a page), which breaks the code because indentation is significant. The complete script already ships with the IDE: open the Walkthrough-Py sample (File → Open → Documents\Whisker Projects) and copy the run(ctx) body from its Python editor, where the indentation is intact. The listing here is for reading along.

    """Motor controller — Walkthrough 2
    
    Implements the AHO state machine, alarm latching, and one-shot
    Clear Alarms behavior described in the Walkthrough scenario.
    Equivalent in behavior to the ladder version in Walkthrough 1 —
    the five sections below mirror Networks 1–5 of the ladder program.
    """
    
    SCAN_PERIOD_S = 0.05      # 50 ms — matches the I/O scanner cadence
    
    
    def run(ctx):
        ctx.log.info("Motor controller started")
    
        while not ctx.shutdown_requested:
            # --- Read inputs ----------------------------------------------
            float_start   = ctx.read_tag("float_start")
            float_stop    = ctx.read_tag("float_stop")
            float_alarm   = ctx.read_tag("float_alarm")
            clear_alarms  = ctx.read_tag("clear_alarms")
            aho_mode      = ctx.read_tag("aho_mode")
            alarm_latched = ctx.read_tag("alarm_latched")
            call_motor    = ctx.read_tag("call_motor")     # previous scan
    
            # (1) Mode decode — CMP equivalent ----------------------------
            motor_off  = aho_mode < 1     # LT  (aho_mode = 0 → Off)
            motor_hand = aho_mode == 1    # EQ  (aho_mode = 1 → Hand)
            motor_auto = aho_mode > 1     # GT  (aho_mode = 2 → Auto)
    
            # (2) Latch the alarm -----------------------------------------
            if float_alarm:
                alarm_latched = True
    
            # (3) One-shot clear ------------------------------------------
            if clear_alarms:
                alarm_latched = False
                ctx.write_tag("clear_alarms", False)       # auto-release
    
            # (4) call_motor seal-in --------------------------------------
            if float_start:
                call_motor = True
            if float_stop:
                call_motor = False
    
            # (5) Motor output --------------------------------------------
            motor_run = motor_hand or (
                motor_auto and call_motor and not alarm_latched)
    
            # --- Write outputs -------------------------------------------
            ctx.write_tag("motor_off",     motor_off)
            ctx.write_tag("motor_hand",    motor_hand)
            ctx.write_tag("motor_auto",    motor_auto)
            ctx.write_tag("call_motor",    call_motor)
            ctx.write_tag("alarm_latched", alarm_latched)
            ctx.write_tag("motor_run",     motor_run)
    
            ctx.wait(SCAN_PERIOD_S)
    
        ctx.log.info("Motor controller stopped")
  3. Press Ctrl+S.

    Python editor with the motor controller code

A few things worth understanding before you move on:

4.7 Step 6 — Define the HighLevelAlarm alert

Same as the Ladder Walkthrough Step 6. Name HighLevelAlarm, condition float_alarm == True — float_alarm is a BOOL, so the value field is a True / False picker (choose == and True) — Latched unchecked (the alert tracks the condition so every occurrence is logged; your Python code handles the motor-interlock latch), output tag blank. The Alert system has no severity or message field — name, condition, latch, and output tag are the only knobs.

4.8 Step 7 — Build the HMI screen

Same as Walkthrough 1 Step 7 — same five widgets, same bindings, same layout. If you’ve already built it in your Ladder project, you can copy the screen across rather than rebuilding it. The clipboard isn’t shared between two IDE windows, so do it within a single window by switching projects:

  1. Save this Python project (Ctrl+S), then open the Ladder walkthrough’s Walkthrough-Ladder.widez (File → Open) in the same window.
  2. In its Project Tree, right-click Motor → HMI Screens → Main and choose Copy.
  3. Re-open your Python project (File → Open), right-click its HMI Screens node, and choose Paste.

Otherwise build it from scratch following Walkthrough 1’s HMI step.

4.9 Step 8 — Connect and Build

Same as Walkthrough 1 Step 8: connect to the controller (Path A or Path B), then press F5 — one press compiles and downloads (signing first on a managed controller).

This time the build output mentions the Python bundle:

Building project: Motor...
Build succeeded: 11 files generated
Uploading program to target...
Reloading program...
Starting VM...

The controller’s smartcontroller service detects the new python_app.json bundle within a couple of seconds, stops the previous run(ctx) thread (if any), unpacks the new bundle, and starts the new one. You don’t have to restart anything manually.

Output panel showing a Python build

4.10 Step 9 — Test it

Same nine test steps as Walkthrough 1 Step 9 — Hand, Off, Auto seal-in, Auto stop, alarm latches, Hand overrides alarm, Clear Alarms releases. The behavior should be indistinguishable from the ladder version.

The Python walkthrough doesn’t have a Debug Mode (Python isn’t a graph that lights up), so the Tag Monitor is your primary window into what the controller is doing — open it in the bottom panel and filter on float, motor, or alarm to keep the relevant tags on screen while you exercise the floats. If a tag doesn’t move when you expect it to, you can check the Python Log to see what your run() loop saw.

If you have both walkthrough projects on hand, try this: deploy the Ladder project, run through the tests, then deploy the Python project and run through them again. Because the two are different projects, the IDE asks you to confirm replacing the one already on the controller — that’s expected; confirm it. You won’t be able to tell which is running just by watching the HMI — which is the whole point.

4.11 What Python gives you that ladder doesn’t

The motor scenario is intentionally simple, so this walkthrough doesn’t show off what Python is genuinely good for. Some things you could try next, that would be painful or impossible in ladder:

4.12 What’s next

5 Your First WhiskerHMI Project

This walkthrough adds a Windows HMI panel to the Application you built in Walkthrough 1. Instead of creating a new Application from scratch, you’ll open Walkthrough-Ladder.widez and add a second Project to it — a WhiskerHMI project — that runs on a Windows PC and shows the same operator screen the controller’s built-in touchscreen shows.

WhiskerHMI is a viewer + interactor. It has no I/O, no ladder, no Python, no alerts of its own. It reads tags directly from the controller’s arena agent over TCP and writes operator input back. All control intent still lives in the Motor PLC project from Walkthrough 1. WhiskerHMI just shows it.

The result is a self-contained Windows installer (.exe) you can run on a desktop PC, a panel PC on the plant floor, or several PCs at once — every panel sees the same live state from the same controller.

Allow about 15 minutes. You need to have completed Walkthrough 1 first (or Walkthrough 2; the WhiskerHMI side is identical either way).

Path A or Path B? On Path B (a controller that is not managed, which is how a new controller arrives) the panel connects plainly and this walkthrough is complete as written. On Path A (a managed controller — cloud-enrolled, or provisioned through a site CA) the controller accepts only secure connections on its data port, so the panel PC needs a panel identity: in Step 3 tick Secure connection, and after installing the panel in Step 8 issue an identity for that PC from the Security dialog and import it from the panel’s Panel ▸ Import panel identity… menu. The procedure is in The WhiskerHMI PC Application.

5.1 Before you start

5.2 Step 1 — Reopen the Walkthrough-Ladder Application

  1. File → Open Application, pick Walkthrough-Ladder.widez. The IDE loads the Application; the Project Tree shows the Motor project from Walkthrough 1.

Why no Modbus setup this time. Earlier versions of WhiskerHMI required you to expose tags via the controller’s Modbus server and manually map them on the panel side. With ArenaTCP the panel reads the controller’s arena agent directly — every tag is automatically readable, no expose step, no Modbus address bookkeeping. The Modbus server is still there if you need it for third-party SCADA, but WhiskerHMI doesn’t use it.

5.3 Step 2 — Add a WhiskerHMI project to the Application

You already have an Application — Walkthrough. You’re going to add a second project to it.

  1. In the Project Tree, click the Application root node (Walkthrough-Ladder) to select it.

  2. From the toolbar, click New Project (the same button you used in the Ladder Walkthrough to add the Motor project, but now you’re adding a sibling).

  3. In the New Project dialog:

    • Project name: Panel
    • Target hardware: WhiskerHMI
  4. Click Create. The Project Tree now shows two siblings under Walkthrough-Ladder: Motor (the PLC project from the Ladder Walkthrough) and Panel (the new WhiskerHMI project, currently empty).

    Project tree with Motor and Panel siblings

  5. Press Ctrl+S to save the Application.

What WhiskerHMI looks like in the tree. A WhiskerHMI project has no Tasks branch (it runs no ladder or Python) and no Functions — it has Tags, IO Configuration, Alerts, and HMI. The IDE knows the target type runs no control logic and hides the branches that don’t apply.

5.4 Step 3 — Add an ArenaTCP device pointing at the controller

The Panel needs to know which controller to read tags from. ArenaTCP is a direct connection to the controller’s arena agent on TCP port 5000 — no Modbus translation in between.

  1. Panel → Setup → IO Config → click Add Device.

  2. At the top of the dialog, choose ArenaTCP for the device kind (the alternative is IODF-backed Modbus, which is what you used in the Ladder Walkthrough if you have any RTU devices wired in).

  3. Fill in the ArenaTCP form:

    • Instance name: controller
    • IP address: the Nexus.io controller’s address from Walkthrough
      1. (If you started from a shipped sample Panel project rather than building it here, its controller device may carry a placeholder address — set it to your controller’s actual one.)
    • TCP port: 5000 (the controller’s data port, not the 9000 port the IDE uses to program it).
    • Secure connection: unticked on Path B; ticked on Path A (see the box at the top of the chapter).
  4. Click Add Device. The Configured Devices table shows the new controller row with a download icon (Import Tags) next to the delete icon.

    ArenaTCP device added

Why ArenaTCP and not Modbus. Modbus is the right answer when the remote endpoint is a third-party device that doesn’t speak anything else. Talking from one Whisker controller (or HMI) to another, the arena agent is the native protocol — it carries tag names, data types, and qualities, so the panel doesn’t need a hand-maintained Modbus address table to know what each register means.

5.5 Step 4 — Import tags from the Motor project

Now bring the six HMI-facing tags from the Motor project into the Panel. Doing this through the import flow keeps the names, types, and controller addresses in sync — no retyping, no transcription errors.

  1. On the controller row in the Configured Devices table, click the download icon (Import Tags).

  2. The Import Tags dialog opens. The source list at the top shows sibling projects in the current Application. Pick Motor.

    (If your source PLC lives in a separate .widez file — say, you’re building a panel for a controller whose project you don’t have open — click the Pick .widez/.wapp button instead and browse to the file.)

  3. The dialog lists every tag from the Motor project with its type and address. Tick the six HMI-facing tags:

    Tag Type
    float_start BOOL
    float_stop BOOL
    float_alarm BOOL
    motor_run BOOL
    aho_mode INT
    clear_alarms BOOL

    Leave the five tags the HMI doesn’t display unticked: the four internal scratch tags (motor_off, motor_hand, motor_auto, call_motor) plus alarm_latched. None of them is bound to a widget — alarm_latched is the ladder interlock latch, motor_run is the actual outcome, and aho_mode already reflects the operator’s mode selection.

  4. The Prefix field defaults to Motor (the source project name). For this walkthrough there’s only one controller so collisions aren’t an issue — clear the prefix field to keep the original tag names. Step 5 imports the screen from Motor and the bindings reference the bare names (motor_run, not Motor_motor_run).

    Import Tags dialog

  5. Click Import. The dialog reports 6 tags imported and closes.

  6. Open Panel → Tags → Tag Database to verify. All six tags appear with the addresses they had in Motor (%I0, %Q0, %MW0, etc.). The Panel project records the controller device as each tag’s source internally; at build time the IO scanner uses that link to read each tag from the controller’s arena rather than from the panel’s own (empty) local arena.

Why the prefix matters. When a single panel reads from multiple controllers — say three pump stations all running the same Motor app — every controller has its own motor_run, aho_mode, etc. Without a prefix the second import would collide with the first by name. The convention is to prefix each import with the controller’s identity (PS1_, PS2_, PS3_ or Station1_, etc.) so all 21 tags coexist in one panel.

Behind the scenes the IDE allocates each imported tag a unique slot in the panel’s local arena. When you import a tag from PS2, the IDE finds the next free local address in that area — so PS1_motor_run and PS2_motor_run end up at different local slots even though both map to %Q0 on their source controllers. At build time the compile-service emits a per-tag mapping (remote %Q0 → local %Q<whatever>); the panel’s IO scanner reads each remote arena and copies each tag’s value into its mapped local slot. For a single-controller panel like this one, no prefix is fine — the imported tags get the same local addresses they had on the source.

5.6 Step 5 — Copy the HMI screen from Motor

The HMI screen you built in the Ladder Walkthrough Step 7 already exists in the Motor project; you don’t need to rebuild it.

  1. In the Project Tree, right-click Motor → HMI Screens → Main and choose Copy. A notification confirms the screen is on the clipboard.

  2. Right-click Panel → HMI Screens and choose Paste. The Main screen appears under the Panel project with all five widgets and their bindings intact.

    The paste also auto-remaps bindings to whatever prefix you chose in Step 4: if you imported with prefix Motor_, the binding that read aho_mode on Motor’s screen is rewritten to Motor_aho_mode on the Panel’s copy. A notification at the bottom confirms how many bindings were remapped. If multiple ArenaTCP devices in the Panel import from the same source project, the paste asks which controller the screen is for.

    If you haven’t imported the source tags yet, the paste is refused with a dialog listing what’s missing. Import first, then paste again.

  3. Under Panel → HMI → Windows, confirm that Main is the primary window — pasted screens inherit their source’s window role, so this should be the case automatically.

5.7 Step 6 — Build the Panel runtime

  1. Click anywhere inside the Panel project (e.g., select Panel → HMI → Screens → Main in the tree) so the IDE knows which project you want to build.

  2. Press F5. The Output panel shows:

    [INFO] Building project: Panel...
    [INFO] Generating screens.hmi (1 screen)
    [INFO] Generating symbols.json (6 tags)
    [INFO] Generating io_scanner_config.json (1 ArenaTCP source)
    [INFO] Generating app_config.json
    [OK]   Build succeeded: 4 files generated
  3. These four files are written to a temporary build folder, not inside your Application directory — you don’t need to find or manage them. The next step (Generate Installer) bundles them into the installer automatically.

Build vs Generate Installer. Build produces the runtime configuration files. To run the panel on another PC, you generate a Windows installer that bundles the WhiskerHMI runtime executable together with the Panel build output — that’s the next step.

5.8 Step 7 — Generate the Windows installer

  1. Click the Generate Installer icon in the toolbar (a small package / box icon). Make sure the Panel project is selected; the button is only enabled for WhiskerHMI projects.

  2. The Installer dialog opens. Fill in:

    • App name: Walkthrough Motor Panel
    • App version: 1.0.0
    • Publisher: your name or company
    • Output folder: somewhere convenient

    Generate Installer dialog

  3. Click Generate. The IDE invokes Inno Setup; after a few seconds the Output panel reports:

    [INFO] Bundling WhiskerHMI runtime...
    [INFO] Bundling project config (Panel)...
    [INFO] Running Inno Setup...
    [OK]   Installer created: WalkthroughMotorPanel-Setup-1.0.0.exe (24.7 MB)
  4. The installer .exe lands in the output folder. That’s the whole deliverable — a single file you can copy to any Windows PC.

5.9 Step 8 — Install and run on a PC

Copy the .exe to the PC where the panel will live (your laptop works for testing). Double-click it:

  1. User Account Control prompts for permission — click Yes.

  2. The installer wizard runs through License, Destination Folder, shortcuts, Install, Finish. The path shown on the Destination Folder page is informational only in the current installer layout — the app is always installed under the system Program Files area: C:\Program Files\Walkthrough Motor Panel\. (If you need to verify after install, that’s where to look — not at whatever was shown on the wizard page.)

  3. The HMI panel launches automatically. The window opens, fills with the Main screen layout, and starts polling the controller’s arena agent.

    Running WhiskerHMI panel

If the screen comes up blank or shows “Connecting…” for more than a few seconds, the panel can’t reach the controller. Check:

5.10 Step 9 — Test it

Tap the AHO selector switch from OFF → HAND on the Windows HMI panel. The motor LED in the Windows panel turns green — and at the same moment, the controller’s built-in touchscreen shows the same change because both panels are reading the same controller state.

Trigger the high-alarm float (physically or via tag forcing). The alarm latches on both panels. Press Clear Alarms on either panel and both clear.

That’s the point of WhiskerHMI: as many panels as you want, each showing the same live state, all writing back through the same arena agent. Run the panel on three workstations and each operator sees the same thing.

5.11 What’s next

6 Where to go next

You’ve built three projects that touch every major editor in the Whisker IDE — Tag Database, IO Configuration, Ladder, Python, Alerts, HMI Designer, and the Modbus server — plus the deploy pipeline for both the controller and the PC panel. You know enough now to build real things.

The rest of this section points at where to deepen each piece.

6.1 The full user manual

The Quick Start Guide you just finished is an extract from the full Whisker IDE User Manual, which ships as HTML and PDF alongside this guide. The full manual covers both editions of the IDE — Cloud and Offline — and says in each chapter what needs an account.

Chapter What it adds to what you did here
The Workspace every panel, toolbar button and bottom-panel tab (Output, Python Log, Tag Monitor)
Projects and Applications multi-project Applications, what is stored where, projects stored on the controller
Setting Up Your Nexus.io Automation Controller the touchscreen settings, network, serial ports, the controller’s security postures
Target Hardware in the IDE the Nexus.io Automation Controller, the SmartController, WhiskerHMI
Tag Database the full address map, classes, initial values, process variables, retained values that survive a program update, system tags
I/O Configuration and Devices every device kind — Modbus TCP/RTU, ArenaTCP (another controller’s tags), EtherNet/IP PLCs
Ladder Logic Editor the editor itself: networks, wiring, properties, tags from the Properties panel
Function Blocks, Faceplates and Tasks reusable logic, faceplates, periodic / event / freewheel tasks
HMI Designer every widget, PIN login at the panel, trend charts, alert tables
Alerts Editor conditions, latching, output tags, the alert table
Modbus Server and Map Viewer exposing tags, renumbering, the four CSV export formats
Python on the Controller the full ctx API, Python function blocks called from ladder, the SmartController’s MicroPython
Connecting, Deploying and Emulating discovery, the three controller postures, Build & Emulate with the IO Stimulus and HMI Preview tabs, what a signed deploy is
Monitoring and Debugging Tag Monitor, Debug Mode, the Output panel, diagnosing a device that will not talk
Security, Provisioning and Enrolment Path A and Path B in full, the Security dialog, certificates, revocation
Generating HMI Installers packaging a WhiskerHMI panel for other PCs
Project Properties Reference every project setting in one place
Ladder Instruction Reference every instruction — timers, counters, compare, math, process blocks (PID, totalizer, filter…), motor and valve blocks, sequencer, scheduler
Historian and Trends the controller’s 60-day historian, trend widgets, CSV export
The WhiskerHMI PC Application several windows, Trends, operator login and roles

6.2 Exposing tags to Modbus

The controller always runs a Modbus TCP server (slave) on port 502 — there’s no switch to turn it on. Instead you choose which tags it exposes, one tag at a time, so a SCADA system, BMS, or any Modbus master can read and write them.

Expose a tag

  1. Open the Tag Database and select the tag you want to expose.
  2. In the Properties panel, check Expose via Modbus.
  3. The IDE auto-assigns a Modbus address. To override it, type a different address in the Properties panel — a tag whose address you edit by hand is flagged as manually assigned, which the renumber tool (below) can leave untouched.

How tags map to registers

Renumbering the map

To re-pack the addresses, select the Project node, and in its properties click Renumber all auto-assigned tags. In the dialog:

The Modbus Map

As soon as at least one tag is exposed, a Modbus Map node appears under the Project in the tree. It’s generated automatically (you don’t edit it directly) and shows two tables — Holding Registers and Coils — listing every mapped tag with its address, type, and access.

The Export button writes the map out as Generic CSV, Ignition CSV, KEPServerEX CSV, or AVEVA System Platform CSV — a big time-saver when you’re pulling these tags into an existing SCADA system.

See Modbus Server and Map Viewer for the full address conventions and read-only vs. read-write rules.

6.3 The Design Spec

Every project has a Design Spec — a living document under the Project in the tree. It starts from a template with sections for Overview, IO Configuration, Control Logic, HMI Screens, Alarms, and Notes that you fill in as you design the system.

Two parts are generated for you and kept up to date automatically:

These tables live inside marked blocks in the document. The IDE refreshes only what’s inside the markers, so everything you write around them — your design narrative, control strategy, commissioning notes — is left untouched. Edit your tags in the Tag Database and the tables follow along.

6.4 Connecting to a controller

Click Connect in the toolbar, then pick the controller from the discovered list or enter its address. You don’t need a project open to connect.

When you connect, the IDE reconciles the project you have open against the project stored on the controller:

Because the controller stores the editable project source, you can pull a project straight off a device even without a local copy. For IP-sensitive deployments, turn this off per project under Project Properties → Deployment → Store editable source on target.

See Connecting, Deploying and Emulating for discovery details, the three controller postures and Build & Emulate, and Security, Provisioning and Enrolment for Path A and Path B in full.

6.5 Sample projects

The IDE ships with the two completed walkthrough projects. On first launch it copies them into your default project folder — Documents\Whisker Projects — which is also where File → Open and Save As start:

Open either with File → Open; it opens straight to Documents\Whisker Projects.

6.6 When you get stuck

6.7 Support

6.8 What we’d love to hear about

D6 Labs prioritizes new IDE features based on what users actually build. If you’ve extended this walkthrough into something interesting — a real production lift station, a hybrid Python/ladder scheme, an unusual WhiskerHMI deployment — drop us a note. Screenshots and brief descriptions go a long way.

Happy building.