1 Getting Started

Welcome to the Whisker IDE — the design environment for the D6 Labs Nexus.io family of industrial automation controllers. This chapter walks you through downloading and installing the IDE, gives you a tour of the workspace, and ends with a five-minute walkthrough that gets you to a saved project ready to deploy.

1.1 About this manual

This manual covers the Standalone edition of the Whisker IDE on Windows. The Standalone edition is fully offline: it does not require an account, an internet connection, or any cloud subscription. Everything you do — designing programs, configuring I/O, deploying to a controller — happens between your PC and the controller on your local network.

A separate manual covers the Cloud edition of the Whisker IDE for users who want fleet management, cloud-staged deployments, and role-based access control through Whisker.io.

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 200 MB free 1 GB free (for projects and HMI runtime bundles)
Display 1280 × 720 1920 × 1080 or larger
Network Ethernet or Wi-Fi on the same LAN as your controller Same

The Whisker IDE itself does not need an internet connection. You only need network connectivity to reach your Nexus.io controllers during deployment.

1.3 Downloading the IDE

  1. Open your web browser and go to:

    www.d6labs.com/downloads/whiskeride_v1_2_standalone_windows.zip

    This downloads a single zip file (~13 MB) containing the installer plus three short text files.

    Note on versioning. The download URL uses a two-component version (v1_2) for stability — minor bumps within the v1.2.x series reuse the same URL. The installer inside the zip carries the full three-component version (e.g., 1.2.0).

  2. 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 “this file might be dangerous,” choose Keep or Keep anyway. Until D6 Labs’s installer is signed by a commercial code-signing certificate, Windows SmartScreen may flag any new download from us. This is normal and the file is safe.

1.4 Installing the IDE

The download is a zip file, not the installer itself. You need to extract it first.

1.4.1 Step 1 — Extract the zip

  1. Right-click the downloaded whiskeride_v1_2_standalone_windows.zip file.

  2. Choose Extract All…

  3. Pick a destination (the default — a folder with the same name in Downloads — is fine).

  4. Click Extract.

    Windows Extract All dialog

When extraction finishes, you’ll have a folder containing:

File What it is
WhiskerIDE-Offline-1.2.0-Setup.exe The installer you’ll run next
README.txt Quick reference matching this chapter
LICENSE.txt End-user license agreement
CHANGELOG.txt Notable changes in this release

1.4.2 Step 2 — Run the installer

  1. Double-click WhiskerIDE-Offline-1.2.0-Setup.exe.

  2. Windows shows a User Account Control prompt asking permission to make changes. Click Yes.

  3. The setup wizard appears. The default values are correct for almost everyone — you can click Next through each screen.

    Screen What to do
    License Agreement Read it, choose I accept, click Next
    Select Additional Tasks Check Create a desktop icon if you want one. Click Next
    Ready to Install Click Install
    Completing Setup Leave Launch Whisker IDE checked. Click Finish

    Setup wizard ready-to-install screen

  4. Installation takes 10–20 seconds. When it finishes, the IDE launches automatically.

1.4.3 Step 3 — Confirm the install

After the installer finishes, you should see:

You can safely delete the extracted folder and the original zip — the IDE is now installed in C:\Program Files\Whisker PLC IDE Offline\.

1.5 First launch

When the IDE opens for the first time, you see an empty workspace. No login, no account setup, no first-run wizard — the Standalone edition is ready to use immediately.

Whisker IDE on first launch, empty workspace

Take a moment to find the window title in the title bar: it should read Whisker PLC IDE Offline. That’s how you tell at a glance which edition you’re running.

1.6 Core concepts

Before you build anything, it helps to understand the four nouns the IDE revolves around.

Application — A container that groups one or more related Projects. For a single-controller installation, your Application will contain one Project. For a system with several controllers and an operator panel, your Application might contain three or four Projects that share tags and design specs.

Project — A single program for a single controller. A Project knows which kind of controller it targets (its Target). Projects are saved as .widez files (a single-file zip archive containing ladder logic, tags, I/O configuration, HMI screens, and alerts).

Target — The type of controller the Project runs on. The Standalone edition supports three Targets:

Tag — A named variable in your program. Tags have a data type (BOOL, INT, DINT, REAL) and an address in controller memory (%M0, %MW10, %MD4, %MF12). You reference tags from ladder logic, HMI bindings, alerts, and the Modbus map.

1.7 A tour of the IDE workspace

The Whisker IDE workspace is divided into seven main areas. The arrangement is shown below, and each area is described in detail in the chapter The Workspace.

Annotated workspace with all panels labeled

  1. Toolbar — file operations, build, connect, run, deploy. The set of visible buttons depends on the active project’s Target type.
  2. Project Tree — the structure of the open Application and all its Projects.
  3. Editor area — open editors for tags, ladder programs, HMI screens, etc. Multiple editors stack as tabs.
  4. Properties panel — when you click an element in the project tree or the active editor, its editable properties appear here.
  5. Toolbox — context-sensitive palette for the active editor (ladder elements, HMI widgets, etc.).
  6. Output panel — build results, deploy progress, errors and warnings.
  7. Status bar — connection state, current target, dirty/clean indicator.

1.8 What’s next

You now know enough about the IDE to build something real. The next three chapters walk you through three end-to-end projects against the same physical scenario — a single motor controlled by float switches, with an Auto/Hand/Off operator panel and a latched high-level alarm. Each walkthrough uses the same controller, the same I/O module, and the same HMI screen, so you can see how Ladder, Python, and a standalone WhiskerHMI panel each express the same control intent:

If you already have a controller on your LAN, start with Chapter 2. If you’re working without hardware for now, the Ladder and Python walkthroughs (chapters 3 and 4) cover the design steps you can do offline; the Build step requires a connected controller because Build compiles and uploads in a single operation.

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 standalone HMI panel — 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 prod with float switches and watch react.

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. Five inputs, one output, one latched alarm, one operator panel.

2.2 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.2.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.2.2 RS-485 settings

The mSmart module talks Modbus RTU at the following defaults. The IDE will let you change all of these from the Add Device dialog if your hardware is configured differently.

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

2.3 Tags — where they come from

The walkthroughs use seven tags. 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 three 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 Appendix AB of the main 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.4 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 W1 and a matching few lines of app.py in W2:

(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.5 The alarm

A single alert definition in each project:

Field Value
Name HighLevelAlarm
Condition float_alarm = TRUE
Latched Yes
Output tag alarm_latched
Severity Warning
Message High-level float is wet

When float_alarm goes TRUE the alert system sets alarm_latched and records an entry in the alert history. The latch stays set until clear_alarms clears it through the control logic above.

2.6 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.7 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 AC 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 Add Project. A New Project sub-dialog opens. Fill it in:

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

    New Project dialog with Motor / Nexus.io AC selected

  5. Click Add, then Create in the outer Application dialog.

  6. Press Ctrl+S. The IDE prompts for a location to save the Application. Pick somewhere convenient (your Documents folder is fine) and accept the filename Walkthrough-Ladder.widez.

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 → IO Config. The IO Configuration editor opens.

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

  3. Configure the device:

    • IODF: 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 IODF:
      • Port: rs485 (the only RS-485 port on the Nexus.io AC)
      • Slave ID: 1
    • Leave baud rate, parity, data bits, and stop bits at the defaults (19200 / N / 8 / 1) unless your hardware is configured differently.

    Add Device dialog with mSmart-Universal-IO selected

  4. Click Add. The device appears in the IO Configuration table with a cable-style RTU icon to its left.

  5. Click the IO Points tabs below the device table. You should see roughly 30 entries the IDE created automatically — one for every channel on the module. Each tag is named uio_<channel> (so uio_DI1, uio_DI2, …, uio_DO1, …) and has an auto-assigned IEC address.

    IO Points table auto-populated from the device

  6. 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. Click on the uio_DI1 row, then click again on its name to enter edit mode (or use the Rename action in the Properties panel on the right). Change the name to float_start.

  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

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

    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 an initial value of 1, and use it as the comparison operand. The tag never changes at runtime; it’s just how you spell “the integer 1” to the CMP block.

    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 → Programs → Main and double-click the ladder file. The Ladder Editor opens with five empty networks — what other PLC platforms call rungs. You’ll need seven for this walkthrough, so click the + button at the top of the editor twice to add two more empty networks at the bottom before you start.

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 left 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

3.6.1 Network 1 — Decode the operator mode (CMP)

The IDE doesn’t ship a standalone EQ instruction. It ships a CMP function block that performs one comparison and produces three outputs (LT, EQ, GT) on a single call. That’s exactly what we want for AHO — one network gives us motor_off, motor_hand, motor_auto all at once.

  1. Click into network 1 to select it.

  2. From the Toolbox, drag a CMP block (under Compare) onto the first segment. Click it; in the Properties panel set:

    • 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)
  3. The CMP block exposes three output pins on its right side: LT, EQ, GT. Each is a BOOL. Wire each to a coil:

    • LT → drag a Coil (mode Simple) to the terminal slot, tag motor_off. Rationale: aho_mode < 1 means aho_mode = 0, which is Off.
    • EQ → drag a second Coil, tag motor_hand. Rationale: aho_mode = 1 is Hand.
    • GT → drag a third Coil, tag motor_auto. Rationale: aho_mode > 1 means aho_mode = 2, which is Auto.

    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 against 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 Coil to the terminal slot. Set:
    • Tag: alarm_latched
    • Mode: Set/Latch
    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 into network 7.

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

  3. Drag a Coil (mode Simple) to the terminal slot. 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.

    The bottom branch will automatically reconnect to the terminal coil at the right.

    Network 7 — motor_run output

  6. Press Ctrl+S.

Tip: read the network out loud. “Motor runs when (Hand) or (Auto AND tank-calling 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. Fill it in:

    • Name: HighLevelAlarm
    • Condition: float_alarm = TRUE (the condition builder lets you pick the tag and operator from dropdowns)
    • Latched: ✓ checked
    • Severity: Warning
    • Message: High-level float is wet
    • Output tag: leave blank — the ladder logic already does the latching in Network 2 using a Set coil.

    Alert editor with HighLevelAlarm configured

  3. Press Ctrl+S.

Two ways to latch — pick one. The alert system can latch on its own and write the result into an output tag. The ladder Set coil in Network 2 also latches. We’re using the ladder version (output tag blank) so the latch logic is visible in one place. If you’d rather let the alert system own the latch, delete Network 2 and set the alert’s output tag to alarm_latched — but then the Reset coil in Network 3 needs to clear it through the alert’s own reset mechanism instead. The walkthrough sticks with the ladder approach because it’s easier to follow when you’re reading networks.

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. From the Toolbox on the left, drag the widgets onto the canvas in roughly this layout (precise positions don’t matter):

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

    • selectorSwitch:

      • Bind positionaho_mode
      • Positions: 3
      • Labels: OFF, HAND, AUTO
    • pump (motor run):

      • Bind statemotor_run
      • Label: Motor running
      • On color: green, Off color: dark gray
    • text (motor label):

      • Label: Motor
    • led × 3 (floats): bind each one to float_start, float_stop, float_alarm respectively. Labels: Start Float, Stop Float, High Alarm.

    • pushButton:

      • Bind outputclear_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.

  2. When your controller appears in the list, click it and click Connect. If mDNS is blocked on your network, click Enter address manually and type the controller’s IP address.

  3. A confirmation snackbar appears at the bottom of the IDE. The status bar now shows the connection.

  4. Press F5 (or click the Build icon). The Output panel at the bottom shows the compile, then the upload to the controller:

    [INFO] Building project: Motor...
    [INFO] Compiling ladder programs (1)...
    [INFO] Compiling alerts (1)...
    [INFO] Generating app.io for I/O scanner...
    [OK]   Build succeeded: 5 files generated
    [INFO] Uploading to 192.168.1.244...
    [OK]   Upload complete (32 KB)
  5. The controller restarts the runtime services automatically and begins executing the new program.

    Output panel showing successful build + upload

3.10 Step 9 — Test it

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

  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 — High-level float is wet, and the motor turns off (if it was on in Auto). Confirm the alarm latches by opening the float — the indicators should stay on.

  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 alarm — Tap Clear Alarms. The alert table clears, the Alarm LED goes dark, and if you’re in Auto with the start float open, the motor stays off until you re-trigger the start 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 AC hardware, 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 AC on your LAN, 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 → IO Configuration, click Add Device, pick the mSmart-Universal-IO IODF, 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.

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

Same as Walkthrough 1 Step 4 — same seven memory tags. The Python code in Step 5 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 double-click app.py. The Python Editor opens.

  2. Replace the entire contents of the file with:

    """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 Walkthrough 1 Step 6. Name HighLevelAlarm, condition float_alarm = TRUE, latched, message High-level float is wet, output tag blank (the Python code handles the latch).

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, the fastest path is:

  1. Open the Ladder walkthrough’s Walkthrough-Ladder.widez in a second IDE window (File → Open).
  2. In its Project Tree, right-click Motor → HMI → Screens → Main and choose Copy.
  3. Switch back to this walkthrough’s window, right-click your project’s HMI → Screens node, 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, press F5.

This time the build output mentions Python:

[INFO] Building project: Motor...
[INFO] Compiling alerts (1)...
[INFO] Bundling Python application (app.py, 1.2 KB)...
[INFO] Generating app.io for I/O scanner...
[OK]   Build succeeded: 4 files generated
[INFO] Uploading to 192.168.1.244...
[OK]   Upload complete (28 KB)

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.

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. 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 standalone 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).

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 Add Project to Application (the same button you used in W1 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 Add. The Project Tree now shows two siblings under Walkthrough-Ladder: Motor (the PLC project from W1) 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 Programs branch (no ladder), no Functions, no Alerts — only Tags, IO Configuration, and HMI. The IDE knows the target type doesn’t support those features and hides the irrelevant branches.

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 → IO ConfigurationAdd 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 W1 if you have any RTU devices wired in).

  3. Fill in the ArenaTCP form:

    • Instance name: controller
    • IP address: the Nexus.io AC’s IP from Walkthrough 1.
    • TCP port: 5000 (the arena agent default).
  4. Click Add. 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 seven 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 — switch to From file and browse to the .widez or .wapp.)

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

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

    Leave the four internal scratch tags (motor_off, motor_hand, motor_auto, call_motor) unticked. The HMI doesn’t need them — 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 7 tags imported and closes.

  6. Open Panel → Tags → Tag Database to verify. All seven 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 W1 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 (7 tags)
    [INFO] Generating io_scanner_config.json (1 ArenaTCP source)
    [INFO] Generating app_config.json
    [OK]   Build succeeded: 4 files generated
  3. The IDE puts the build output in a build/Panel/ folder under your Application directory. The Motor project’s build is in build/Motor/ — they don’t interfere.

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 (C:\Program Files\Walkthrough Motor Panel\ by default), shortcuts, Install, Finish.

  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 PLC and HMI targets. 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. The full manual covers:

Chapter Topic
2 The Workspace — every panel, every toolbar button, every keyboard shortcut
3 Projects and Applications — multi-project layouts, sharing tags
4 Controller setup — first-time provisioning, network, time
5 Target hardware — every supported HDL, ports, expansion
6 Tag Database — full address map, classes, types, PVs
7 IO Configuration — every supported IODF, polling, error handling
8 Ladder editor — every element, function blocks, custom FBs
9 Functions and tasks — periodic, event-driven, freewheel
10 HMI Designer — every widget, themes, layout, multi-screen flow
11 Alerts editor — conditions, severity, output tags, latch policies
12 Modbus server — address allocation, range customization
13 Python editor — the full ctx API, threading, dependencies
14 Connecting and deploying — security modes, discovery, OTA
15 Monitoring and debugging — Tag Monitor, journalctl, AI Fix
16 Security — PKI, signed bundles, RBAC
17 Generating installers — WhiskerHMI panel deployment in detail

The full manual lives at d6labs.com/docs/whisker-ide-manual and ships as an HTML and PDF alongside this Quick Start Guide.

6.2 In-IDE help

6.3 Sample projects

The IDE ships with a handful of reference projects you can open and inspect:

Open them with File → Open Sample Project.

6.4 When you get stuck

6.5 Community and support

6.6 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.