mcplc edits the HAL of a machine like a PLC program, after the pattern of SINUMERIK/TIA Portal: program blocks of networks, each a function block diagram (FUP) of instruction boxes (&, >=1, =1, SR, TON, comparators, arithmetic …​) and HAL components wired by signals, with pins of the machine’s base components (motion., halui., joint.N.) and named signals as operands. The project (.hpj, JSON) is the source: the HAL file is generated from it, and existing HAL files are imported into networks. With LinuxCNC (or halrun) running, the live view colours true bits green and shows the values at the pins. Start with mcplc [project.hpj | file.hal]; mcplc file.hal --generate out.hal regenerates without the window (man page mcplc(1)).

Window

  • Projektnavigation (left): the project with its Baugruppen (the base HAL lines: loadrt of motion, kinematics, drivers, their addf), the Programmbausteine with their networks, Geraete & Netze (the configured cards with their daughter cards), the IO-Variablen, the SCL blocks (stage 2) and the Variablentabelle (all signals of the project).

  • Editor (centre, tab Programmbausteine): the networks of the selected block stacked, each with its title and comment; a toolbar with the frequent instructions (&, >=1, =1, NOT, SR, TON), the operands (pin, signal, constant) and the FUP/KOP switch. The Info pane shows the pins of the selected box, messages and the HAL preview.

  • Geraete & Netze (centre, second tab): the hardware view, see Devices and networks (hardware).

  • Anweisungen (right): the instruction palette by groups (Bitverknuepfungen, Zeiten, Flanken, Vergleicher, Mathematik, Verschieben, Filter, Sicherheit), all HAL-Komponenten of the library with their pins, and the operands; a double click inserts into the selected network. The search field above filters by symbol, name, component or description (several words narrow down; Return inserts the first hit).

  • Testen (right): the live view switch, setting a pin/signal value (setp/sets), the list of the running HAL’s pins.

  • Hardware-Katalog (right): the Mesa cards and daughter cards of the catalogue with their firmware variants; a double click adds a card to Geraete & Netze.

Editing

Boxes show the names of their inputs and outputs inside, as TIA Portal does (IN1, IN2, OUT; S, R1, Q; IN, PT, Q, ET; the pin names of a HAL component), and are dragged on a grid. A wire is pulled like in TIA: press on an output port, drag to the input port (the target is ringed while dragging) and release - releasing on a box picks its nearest free input, releasing on an operand connects the operand. A click on a port followed by a click on another port wires them as well. Resting the mouse on a port shows an info box with its name, data type (Bool/Real/DInt/UDInt with the HAL name bit/float/s32/u32), direction and HAL pin; the type of an operand comes from the library, the running HAL or the configured hardware. While pulling, the target is ringed when the types fit and crossed with a red X when they do not (a float output onto a bit input, say) - such a wire is refused, as is wiring two inputs together; a constant fits every type it can be written as. An output may feed several inputs. A click selects a box or operand (bold blue frame) or a wire (bold line); Entf deletes the selection - a wire’s target leaves the signal, the other endpoints stay connected, a box goes with all its wires. Inputs and in/out pins sit on the left of a box, pure outputs on the right. Operands are edited by a double click (pin names come from the running HAL or from the library’s patterns). The context menu of a box adds or removes inputs of &/>=1/=1, negates an input (drawn as a circle, generated as a not instance), renames the HAL instance and edits the parameters written with setp; the context menu of a wire names its signal or removes it; the context menu of the background inserts, edits, disables or deletes networks and runs the automatic layout. Ctrl+Z/Y undo and redo.

Instructions and HAL

Box HAL component Note

&, >=1, =1, &¬, >=1¬

logic

personality = function + number of inputs (2..16)

NOT

not

SR

flipflop

S = set, R1 = reset (dominant), Q, QN

TOGGLE

toggle

TON, TOF

timedelay

PT in seconds on the on-delay / off-delay pin

TP

oneshot

pulse of PT seconds

P_TRIG, N_TRIG

edge

in-edge parameter

>, <, ==, IN_RANGE

comp, wcomp

ADD, SUB, MUL, DIV, ABS, SCALE, LIMIT, HYPOT

sum2, mult2, div2, abs, scale, limit1, hypot

MOVE, SEL, MUX

scale (gain 1), mux2, mux4

LOWPASS, INTEG, ESTOP

lowpass, integ, estop_latch

any library component

the component with all its pins

The generated file holds the device lines (Devices and networks (hardware)) and the base lines first, then one loadrt …​ names=…​ per component with all instances of the networks, addf in block and network order on the block’s thread (the read functions of the cards before, their write functions after everything else), the net lines of the IO variables, and per network the setp of constants and parameters and the net lines: a connected group of wires is one signal, named by the user, by a signal operand, or after its driving pin. Instances without a name get <box>-<block>-<network>-<k>; a negated input becomes a not instance in the generated file only, the project keeps the circle.

Threads: fast for the drives, slow for the PLC

Time-critical functions - the trajectory planner (motion-controller, always in motmod’s servo thread), the PID loops, the read/write functions of the cards and the probe input - run in the fast thread; the PLC logic (interlocks, emergency stop chain, operator panel, spindle enables, tool change sequences) runs in a slow one. The project keeps a thread table (Bearbeiten > Threads (Zykluszeiten), also the node Threads of the navigation): name and period. A new project has servo-thread 1 ms (created by motmod from [EMCMOT]SERVO_PERIOD) and plc-thread 10 ms; more can be added (a 0.25 ms fast-thread for an encoder filter, say). HAL wants the fastest thread first and the others as integer multiples; the generator writes

loadrt threads name1=plc-thread period1=10000000

for the threads no base component creates, fastest first, after the base loadrt lines (a project without motmod gets all its threads from this line). Each block has a thread (Bearbeiten > Thread des Bausteins; new PLC blocks take the slowest), and a network may override it (context menu Netzwerk-Thread, shown as a clock mark and the thread name at the right of the network header) - the probe network in a PLC block goes to the servo thread this way. Per thread the generated order is: card read functions, motion, the instances of the networks in block and network order, card write functions. Signals between threads are plain HAL signals. The CNC assistant (The CNC assistant) puts the axis, spindle and probe networks in the fast thread and the emergency stop chain in the PLC thread, and writes [EMCMOT]SERVO_PERIOD and PLC_PERIOD into the INI.

HAL files as units (OB) and folders

A project is made of units, one per HAL file, the way a Siemens program is made of organisation blocks: Bearbeiten > HAL-Dateien (Einheiten) lists them with their kind - hal (loaded by the run script, [HAL]HALFILE), postgui (loaded after the GUI, [HAL]POSTGUI_HALFILE, the place for panel pins) or external (a Tcl/LIB: file that stays untouched in the INI). Every block belongs to a unit (HAL-Datei des Bausteins) and may sit in a folder path (Ordner des Bausteins, Zylinder/Vorschub), both shown in the navigation. The generator writes one file per unit: the first carries the devices, the base lines, the threads and every loadrt (a module loads only once), each file the addf/net/setp of its blocks, and the card write functions close the last pre-GUI file. HAL-Datei(en) generieren asks for a directory when there is more than one unit; mcplc project.hpj --generate DIR does the same without the window. The CNC assistant writes the matching HALFILE/POSTGUI_HALFILE lines.

Ladder view (KOP)

The FUP/KOP switch of the toolbar changes how a block’s networks are drawn; the graph behind them is the same, and the generated HAL does not change. In KOP a network is drawn between the power rails: bit operands that feed & boxes become contacts in series (--| |--), those feeding >=1 boxes parallel branches, a negated input an open contact (--|/|--), NOT boxes vanish into the contacts, a written bit operand becomes a coil (--( )--) at the right rail. Other boxes (=1, SR, TON, counters, components, FB instances) stand in the rung with their bit inputs fed by contact logic, their other inputs by operands and their outputs feeding coils. A network without that form - arithmetic and float signals only - keeps its FUP drawing inside the KOP block with a note, as TIA Portal refuses such a switch.

Editing in KOP works on the graph: --| |-- inserts a contact in series with the selected one (an & appears in FUP), --|/|-- an open one, --( ) a coil driven by the selected contact or box output, Zweig a parallel branch to the selected contact (a >=1); the context menu of a contact swaps closed and open (the negation of the input it feeds, or a NOT before its coil). With the live view on, contacts, rung lines and coils are green when their signal is true.

Function blocks (FB) and data blocks (DB)

The context menu of Programmbausteine (and of a HAL file, a folder or a block in it) offers Neuer Programmbaustein, Neuer Funktionsbaustein (FB), Neuer Datenbaustein (DB) and Neuer Ordner, as the TIA Portal project tree does.

A function block is a block with an interface (context menu Schnittstelle…​): parameters with a name, a direction - in, out, inout or static (a memory of the instance) -, a data type (Bool/Int/DInt/UInt/UDInt/Real/Time, mapped to bit/s32/u32/float), a start value and a comment. Inside the FB the parameters are operands #name (toolbar #Param): an input is a source on the right, an output a target on the left, inout and static both. The FB appears in the palette under Eigene Bausteine (FB); a double click puts an instance into the current network, named like an instance DB (zyl1), drawn as a box with the interface parameters as ports and typed like any other box. A cylinder control with five inputs and two outputs is written once and used as often as needed; FBs may use other FBs (recursion is refused). There is no separate FC: an FB without static parameters is one.

The generator expands every instance: the networks of the FB are copied behind the calling network (titled zyl1: Ventil), their HAL instances get the instance name as prefix (zyl1-ton-2), the parameters become signals zyl1-start, zyl1-ventil …​ and the ports of the instance box are replaced by signal operands of the same names, so a wire group stays one signal; a user-named signal on an instance port keeps its name. A constant on an instance parameter becomes newsig/sets in the first HAL file. Instanz oeffnen is not needed: the generated file shows the expanded networks.

A data block is a table like in TIA (double click on the DB): Name, Datentyp, Startwert, Remanenz, Kommentar, with Struct elements that hold children and Array[lo..hi] elements. Every leaf is one typed HAL signal - dbDaten.zaehler, dbDaten.pos.x, dbDaten.flags.3 - created in the first HAL file with newsig (HAL’s current type names bool, sint, uint, real) and, with a start value, sets. Networks read a DB element through a signal operand of that name (the signal browser lists all DB elements with their types, so the type check applies) and write it with a wire from an output onto the operand; HAL allows one writer per signal. String, Char and Date do not exist in HAL and are refused. The Remanenz column is stored; saving and restoring retentive values comes with a later step. With the live view on, the DB table shows the actual values.

PLC variable tables

PLC-Variablen (navigation node and centre tab) lists every variable of the project with its table, data type, address and comment: the signals of the networks, the IO variables with their terminal pin as address, and the DB elements. The Standard-Variablentabelle holds all variables by default; Neue Variablentabelle adds tables to structure them (Sicherheit, Hydraulik …​), and a double click on the Variablentabelle cell of a row opens the dropdown that assigns the variable, like in TIA; a double click on the comment edits it. The navigation shows every table with its count, Alle Variablen anzeigen the whole list.

Import of HAL and INI files

Projekt > INI importieren reads a machine’s INI: every HALFILE and POSTGUI_HALFILE becomes a unit with one block ([HAL]HALCMD lines a unit halcmd.hal, Tcl/LIB: files external units), [EMCMOT] SERVO_PERIOD/BASE_PERIOD the thread periods, [DISPLAY]PYVCP / GLADEVCP and loadusr pyvcp/gladevcp lines the panels whose pins (pyvcp.spindle-at-speed, gladevcp.x_plus) are read from the panel files and known to the pin browser. The components of the base lines - loadrt of motion/kinematics/drivers and loadusr programs such as halui, hal_manualtoolchange, pyvcp - are listed under Baugruppen > Komponenten with their pin names (from the library or the running HAL). mcplc machine.ini opens the import directly; the project is saved next to the INI.

Projekt > HAL-Datei importieren reads loadrt/addf/net/setp: instances of catalogue components become boxes (a logic with personality 0x103 is a three-input &, a timedelay with off-delay set a TOF …​), other library components generic boxes, pins of base components operands, setp on an instance pin a constant. Networks are the connected groups of instances over the nets; a net without instances is a network of its own. The comment above a net becomes the network title. The round trip import - generate keeps every net with its pins (tests/mcplc).

The library

Pins, parameters and functions come from the .comp sources of src/hal/components, from the man pages of the C components and, when connected, from the running HAL (hal.get_info_pins); the result is cached in ~/.config/mcplc/components.json (Online > Bibliothek neu einlesen).

Devices and networks (hardware)

The tab Geraete & Netze assembles the hardware like the device view of TIA Portal: the PC with its servo thread, the Mesa FPGA cards on their bus (PCI or Ethernet) and the daughter cards hanging on the sserial channels (RJ45/DB25) of a card. A double click on a card in the Hardware-Katalog adds it; a click selects it and shows its properties:

  • Firmware / Konfiguration: the firmware variant decides what the connectors carry. A 5i25 with 7i76x2 has ten step generators and two encoders for two 7i76 stepper cards; with 7i77x2 six encoders for two 7i77 servo cards with analogue outputs; 7i77_7i76 one of each, G540x2, prob_rfx2 …​ Changing the firmware changes the pins of the card and the sserial channels on which daughter cards fit.

  • the number of used step generators, encoders and PWM generators (num_stepgens=, num_encoders=, num_pwmgens=); the pins of the unused channels are plain GPIO.

  • IP-Adresse (Ethernet cards, board_ip=) and the watchdog timeout.

  • one combo box per sserial channel with the daughter card and its mode (7i76 mode 0/2, 7i77 mode 0/3, 7i73 mode 1, 7i84, 7i70, 7i71, 7i69, 7i64, 8i20); a 7i77 occupies its channel and the next one (analogue part). Changing the firmware puts the cards it was made for on the channels. The result is the sserial_port_0= string with one digit (the mode) per channel of the firmware, x for an unused channel.

The config string is shown at the bottom of the properties and goes into the generated HAL as

loadrt hostmot2
loadrt hm2_pci config="num_encoders=6 num_pwmgens=0 num_stepgens=5 sserial_port_0=00x0x"
loadrt hm2_eth board_ip="10.10.10.10" config="num_encoders=2 num_pwmgens=0 num_stepgens=10 sserial_port_0=0x0x"
setp hm2_5i25.0.watchdog.timeout_ns 5000000
addf hm2_5i25.0.read servo-thread     # first in the thread
...
addf hm2_5i25.0.write servo-thread    # last

(a 5i25 with 7i77_7i76: the 7i77 on P3 takes channels 0 and 1, the 7i76 on P2 channel 3; firmware= is written for boards that load their firmware over the bus, not for the 5i25/6i25/7i90 with it in flash or for Ethernet boards). All HAL pins of the configured hardware (hm2_5i25.0.stepgen.00., hm2_5i25.0.encoder.00., hm2_5i25.0.gpio.014.in, hm2_5i25.0.7i77.0.0.input-05, hm2_5i25.0.7i77.0.1.analogout0, hm2_5i25.0.7i76.0.3.spinout …​) are known to the pin browser with their direction and their location (connector pin or terminal).

Finding cards

Karten suchen…​ finds the cards of the computer: PCI through lspci (Mesa’s vendor id 0x2718 and the older boards behind a PLX bridge with their subsystem ids, as hm2_pci knows them) and Ethernet through an LBP16 board-info read on UDP port 27181 - the same packet hm2_eth probes with - sent to every host of the given /24 subnets in parallel (the host’s own networks plus Mesa’s defaults 10.10.10.0/24 and 192.168.1.0/24 are preset). Firmware der Karte lesen runs mesaflash --readhmid on the selected card and matches its pin table (StepGen, QCount, PWM, SSerial per I/O index) against the catalogue’s firmware variants; a known variant is set, an unknown one is written as a user catalogue file to ~/.config/mcplc/hardware/mesa-<board>-<name>.xml with the pins read from the card, and the card appears with that variant. Einfuegen puts the card into Geraete & Netze with its IP.

Flashing (mesaflash)

Firmware…​ on a selected card opens the mesaflash dialog: the bitfile (preset from the variant’s file in /lib/firmware/hm2/<board>/, or any .bit), Info (--info), Pinbelegung lesen (--readhmid, offers to switch the project to the variant found), Schreiben (--write, verified by mesaflash; Fallback-Bereich adds --fallback), Pruefen (--verify), Neu laden (--reload), Reset, Backup (--backup-flash), sserial-Karten (--sserial) and, for Ethernet cards, IP setzen (--set ip=). PCI cards need root: mcplc runs mesaflash through sudo -n (or pkexec); without the right it prints the command to run by hand, and without mesaflash installed it prints the command as well. After a successful write the card’s variant in the project is switched to the one the file belongs to, so the pins follow.

IO variables

The table IO-Variablen gives terminals symbolic names, like the PLC tag table: a name (the HAL signal), the terminal pin, type and comment. Alle Klemmen anlegen creates one per input/output terminal of the selected card (i525_input00 = hm2_5i25.0.7i77.0.0.input-00, q525_output03, i525_gpio014 …​), the names are edited by a double click. The names are operands in the networks (signal operand) and are generated as net <name> <pin>, so the networks never carry the terminal pins themselves and rewiring a machine changes one table.

Hardware catalogue

The cards are described in XML under configs/common/mcplc_hardware/ (installed to share/linuxcnc/mcplc_hardware): one file per board (<board name title driver bus> with <firmware name file encoders pwmgens stepgens sserial_channels watchdog gpio>, its <connector> elements and their <pin io type chan hal dir sserial_channel default_card>), and mesa-daughters.xml with the daughter cards (<daughter name mode title channels>, <terminal name>, <pin n hal htype dir channel>). The shipped files are generated from the tables of pncconf by src/emc/usr_intf/mcplc/gen_mesa_catalog.py; a card or a custom firmware is added by writing another XML file.

EtherCAT: drives and terminals

EtherCAT devices are described by ESI files (EtherCAT Slave Information, ETG.2000, XML <EtherCATInfo>) - the counterpart of the Profinet GSD file, shipped by every vendor (Beckhoff as one ZIP of all terminals; Omron, Delta, Leadshine, RTelligent, Inovance …​ per product). Hardware-Katalog > ESI-Dateien importieren reads XML files or ZIP archives into the user catalogue ~/.config/mcplc/esi/; a small set (EK1100, EL1008, EL2008, EL3064, EL4034, EL5101 and a generic CiA 402 drive) ships with mcplc. The catalogue knows per device the vendor and product ids, revision, name, group, profile, the sync managers, the RxPdo/TxPdo definitions with their entries (index, subindex, bit length, name, data type) and the distributed-clock modes.

In Geraete & Netze the EtherCAT-Master hangs on the PC with its slaves as a chain in bus order; a double click on a device of the catalogue appends a slave, EtherCAT-Bus scannen reads the real bus through the IgH tool (ethercat slaves -v, ethercat pdos; the output can also be pasted) and matches it against the catalogue - unknown slaves become generic ones with the PDOs the bus reports. Per slave: the name (HAL prefix lcec.0.<name>.), the lcec driver - the dedicated driver of linuxcnc-ethercat when it has one for the type (EL1008, EL2008, EL3064, AX5xxx, ECT60 …​), basic_cia402 for CiA 402 drives (profile 402 or the 0x6040/0x6041 objects), generic for the rest -, the distributed-clock mode from the ESI, CiA 402 modParam settings (homeMethod, homeOffset, profileMaxVelocity …​), SDO start parameters and, for generic slaves, the PDOs taken into the configuration. The HAL pins of every slave are known to the pin browser: lcec.0.din1.din-3, lcec.0.ain1.value-0, lcec.0.xaxis.srv-cia-statusword …​

The generator writes ethercat-conf.xml for lcec_conf next to the HAL files - <masters><master idx appTimePeriod refClockSyncCycles> <slave idx type name [vid pid configPdos]> with dcConf, sdoConfig, modParam and, for generic slaves, syncManager/pdo/pdoEntry with halPin/halType from the ESI entries - and the HAL lines loadusr -W lcec_conf ethercat-conf.xml, loadrt lcec, addf lcec.read-all first and addf lcec.write-all last in the fast thread. The CNC assistant knows the axis type ethercat: a cia402 box (the hal-cia402 component, pins pos-cmd, pos-fb, enable, statusword, controlword, opmode, drv-target-position …​) sits between the joint and the srv-* pins of the drive, with the position scale and the INI entries for drive-internal homing. linuxcnc-ethercat and the IgH EtherCAT master are installed separately (https://github.com/linuxcnc-ethercat/linuxcnc-ethercat, etherlab.org).

The CNC assistant

CNC-Assistent…​ in Geraete & Netze builds the base program of a machine from a table: for each axis the letter, joint, type (stepper or servo), the card and its channel, the scale (steps or counts per mm), Vmax, Amax and the limits; the spindle (none, PWM channel or analogue output such as hm2_5i25.0.7i76.0.0.spinout) with its maximum speed; the emergency stop input; the probe input; the periods of the servo and PLC threads. It creates the block CNC (first block, servo thread) with one network per axis - stepper: joint.N.motor-pos-cmd to stepgen.NN.position-cmd, feedback, enable, the stepgen parameters (scale, maxvel/maxaccel with 25 % headroom, step timing) - servo: a pid box with encoder.NN.position as feedback and the output on pwmgen.NN.value or, with a 7i77 and no PWM in the firmware, on 7i77.0.N.analogoutN -, the spindle network (spindle.0.speed-out-abs, spindle.0.on, direction, scale), the probe network (input, optionally inverted, to motion.probe-input, in the fast thread), and the emergency stop chain (iocontrol.0.user-enable-out and the input through an ESTOP latch to iocontrol.0.emc-enable-in, in the PLC thread). The networks are then edited like any other. The matching INI ([KINS] trivkins with the coordinates, [TRAJ], [AXIS_x], [JOINT_n] with scale, limits, stepgen headroom, [SPINDLE_0], [HOSTMOT2] with the config strings, [HAL] with the generated HAL file) is saved with Projekt > INI der CNC speichern.

SCL blocks: structured text compiled to HAL components

Eigene Bausteine (SCL) (navigation, context menu Neuer SCL-Baustein) hold blocks written in the structured-text subset below; each becomes one HAL component through halcompile, used in networks like any component - a box with the parameters as ports, in the palette under Eigene Bausteine (SCL):

FUNCTION_BLOCK "Zylinder"
VAR_INPUT  start : Bool;  zeit : Real := 2.5;  END_VAR
VAR_OUTPUT aktiv : Bool;  rest : Real;         END_VAR
VAR        t : TON;  flanke : R_TRIG;  zaehler : DInt;  END_VAR
BEGIN
    t(IN := start, PT := zeit);
    flanke(CLK := start);
    IF flanke.Q THEN zaehler := zaehler + 1; END_IF;
    aktiv := t.Q;
    rest := MAX(zeit - t.ET, 0.0);
END_FUNCTION_BLOCK

VAR_INPUT parameters become pin in, VAR_OUTPUT pin out, VAR_IN_OUT pin io (types Bool, Int, DInt, SInt, UInt, UDInt, USInt, Byte, Word, DWord, Real, LReal, Time - bit, s32, u32, float), VAR static variables that keep their value between cycles; the body runs once per thread cycle as the component’s function. Statements: :=, IF/ELSIF/ELSE/END_IF, CASE …​ OF with single values, lists and ranges, FOR …​ TO …​ BY …​ DO, WHILE …​ DO, REPEAT …​ UNTIL, RETURN, EXIT. Expressions: + - * / MOD **, AND OR XOR NOT, = <> < ⇐ > >=, numbers, TRUE/FALSE, time literals T#1s500ms (seconds), PERIOD (the cycle time in seconds), functions ABS MIN MAX LIMIT SEL SQRT SIN COS TAN ATAN EXP LN TRUNC ROUND and the conversions REAL_TO_INT etc. Built-in instances declared under VAR: TON, TOF, TP (IN, PT → Q, ET), R_TRIG, F_TRIG (CLK → Q), CTU, CTD (CU/CD, R/LD, PV → Q, CV), SR, RS (Q1); they are called as t(IN := x, PT := 2.0) and read as t.Q.

The editor shows the translated .comp (Uebersetzen), Compilieren & installieren runs halcompile --install on it (with sudo when the RT library directory is not writable) and puts the component into the library; .comp speichern writes the file for a build by hand. A box of a block that is not installed yet is a reminder in the log: the generated HAL loads it with loadrt <name> names=…​ like every component, so the machine needs the installed component.

Simulation, retentive values, cross references

Testen > Simulation starten writes the HAL files of the project in a simulation form into sim/ next to the project and runs them in halrun: the base components (motion, kinematics), the cards, the EtherCAT slaves and the panels are left out and their pins leave the nets - the signals stay, listed in the log, and are set by hand with Wert setzen (sets) -, the threads are created by the file, the networks, data blocks and constants are loaded as they are. The live view switches on by itself, so the logic can be watched and driven without a machine; Simulation stoppen (or closing mcplc) ends halrun.

A DB element with Remanenz keeps its value across restarts: the generated HAL loads the userspace component mcplc_retain right after the DB signals (loadusr -W mcplc_retain <machine>.retain.json dbDaten.zaehler …​), which restores the saved values with sets before the networks give the signals writers, watches them, writes the JSON file whenever one changes (at most once per second) and once more when HAL shuts down. The file lives in the configuration directory.

Online > Querverweise (and Querverweise…​ in the context menu of an operand or box) lists every signal, pin, parameter, instance and IO variable of the project with the blocks and networks that use it, read or written; a double click jumps to the network and selects the element.

Traces

Traces in the navigation (context menu Neuer Trace) record HAL signals over time like the trace function of TIA Portal. The Trace page shows on the left the signals of the trace - chosen from the dropdown of the project’s variables and the running HAL’s signals, or any pin of a component through Pin…​ (a pin already on a signal is recorded through that signal, a free pin gets a trace signal of its own) - and the recording conditions: the resolution (sample interval in ms), the duration, the trigger (none, rising or falling edge of a bit, a level above or below for numbers) with the pre-trigger time kept before it, and the mode. In mode sampler the recording is done in real time: mcplc loads the sampler component into the fast thread, links the signals to its pins and reads halsampler, one sample per thread cycle decimated to the resolution; mode poll reads hal.get_value from the GUI (10 ms and slower) when realtime sampling is not possible. Aufzeichnung starten arms the trigger and the chart follows the recording live (Automatisch); stoppen ends it early.

The chart draws one track per signal (bits as 0/1 steps, numbers scaled to their range) over the time axis; the mouse wheel zooms, the scrollbar pans, Alles shows the whole recording. Two measuring cursors work like on an oscilloscope: a left click sets t1, a right click (or Shift+click) t2, both are dragged with the mouse; the header shows t1, t2 and dt, the signal table Y(t1), Y(t2) and dY per signal. Speichern writes the recording as CSV (t_s and one column per signal) together with a .trace.json of the configuration, Laden brings a saved recording back into the chart.

Ansicht switches the chart: Zeit is the time diagram, FFT the amplitude spectrum of every signal (Welch estimate, dB over a logarithmic frequency axis up to half the sample rate), Bode the transfer function of an output signal over an input signal (magnitude, phase and coherence) - the "Transfer" of TIA’s maths function, e.g. the frequency response of the mechanics from the motor and the load encoder of a trace. The two cursors read level, phase and their differences in the frequency views; polled recordings are put on a uniform time grid first.

Axis tuning (position loops)

Every measurement and the automatic tuning move the axis. The operator keeps the responsibility: working area free, parts fixed, the axis enabled, a small amplitude first. mcplc asks before every start.

Achsoptimierung in the navigation (context menu Neue Achse) and the page of the same name tune the position loops of the pid instances the way the measuring functions of TIA Portal / Startdrive do, on the components LinuxCNC has:

  • Achse: the pid instance (from the running HAL or the pid boxes of the project), its thread; Erkennen reads the command, feedback and output signals and the cycle time from the running HAL.

  • Regler (pid): Pgain, Igain, Dgain, FF0-FF2, bias, deadband, maxerror, maxoutput read from and written to the HAL (setp).

  • Filter im Reglerausgang: biquad instances between the pid output and the drive - Bandsperre (type 2, f0 and Q = f0 / bandwidth; the bandwidth not narrower than half the frequency, so the notch stays robust when the resonance shifts; the component accepts Q from 0.5 to 2 only) and Tiefpass (type 1); Auf Resonanz makes a notch from a resonance of the findings list, In HAL laden puts the chain into the running HAL (a module loads only once: when the configuration already uses biquad, the filters take effect in the generated HAL), Ins Projekt uebernehmen writes the gains into the pid box and inserts the biquad boxes behind its output.

  • Messfunktion:

    • Regelstrecke - a PRBS noise signal (maximum length sequence, one sample per thread cycle) is added to the pid output after the filters through a summing component inserted into the running HAL (offset, sum2 or scale, whichever is free), fed by streamer/ halstreamer; the feedback is recorded with sampler/halsampler. The frequency response feedback/excitation (Welch estimate, Hann windows, coherence) is measured inside the closed loop; the controller active at that moment is taken out again (G = Gm/(1-C Gm) with C = P + I/s + D s), so the curve shows the mechanics: Resonanz (Pol) and Tilger (Nullstelle) with frequency, level, Q and bandwidth in the findings list.

    • Fuehrungsfrequenzgang - the noise is added to the command (the feedforward is switched off for the measurement and restored). The closed loop Fg gives the bandwidth f(-3 dB) and the resonance peak (at most 3 dB), the open loop Fo = Fg/(1-Fg) the gain margin (6-10 dB at the -180 degree crossing) and the phase margin (about 40 degrees and more at the crossover).

    • Sollwertsprung - a step with a short ramp on the command; rise time 10-90 %, overshoot (4 % for the amount optimum, 43 % for the symmetric optimum), settling time (2 %) and the steady error.

    • Verfahrprofil - a smooth move there and back on the command with the feedforward on (the "Symmetrierung" check: the controller should have nothing left to do during a move); the following error is fitted against the command velocity and acceleration, which gives the FF1 and FF2 the error asks for. FF1/FF2 aus Verfahrprofil applies them and measures once more.

      The Bode plot draws magnitude and phase over a logarithmic frequency axis with the filter chain and the open loop overlaid and the resonances marked; two cursors (left click f1, right click f2) read level and phase and their differences, as on an oscilloscope. The HAL is restored after every measurement (the summing component and the streamer are unloaded, the signals re-linked).

  • Automatisch:

    • Relay (pid-intern) - the auto tune built into the pid component: the output switches between +-effort around the bias, the amplitude and period of the limit cycle give the ultimate gain Ku and period Tu and the component writes Ziegler-Nichols gains (PID, or PI with FF1 = 1 for a velocity drive); Ku and Tu are shown (the pid exports them as pins with debug=1, otherwise they are taken back from the gains). Fully automatic, no HAL changes, but a coarse rule.

    • Frequenzgang (loop shaping) - the plant is measured, notches are put on the resonances above the expected crossover, P is placed where the plant phase leaves the chosen margin (konservativ 60 degrees / 10 dB, standard 50 / 8, dynamisch 40 / 6) and lowered until the gain margin and every resonance peak keep their reserve, I follows Tn = 4/wc of the real crossover (symmetric optimum), FF1 comes from the velocity gain of the plant, and a step of the feedback loop alone (feedforward off) verifies the result: Tn is doubled while the overshoot exceeds 25 %. The report lists every step.

Semi-automatic tuning is the same loop by hand: measure the plant, set a notch on a resonance, raise P (a factor 1.4 is 3 dB), measure the closed loop and read the margins, add I, check with a step.

Planned

Limit and home switches in the CNC assistant, several channels, arrays and structures in SCL, modular EtherCAT slaves, FFT of traces, D gain and command filters in the loop shaping.