Install rt-core on a cell host
On this page
This guide takes a PREEMPT_RT Linux host from a bare OS to running rosie-rt-core and rt-control as systemd services against real drives. It uses the scripts in rt-core/host/ and rt-core/tools/.
Warning
Energised motion. This moves the robot. RosieOS has no software E-stop: keep the hardware E-stop within reach and clear the cell before you arm. Jog, moves and Home are checked against joint limits only, not against collisions. See the safety model.
Installing is owner-operated work. The scripts refuse to start anything on their own: every install leaves the units inactive, and you start them when the cell is ready. Passing every check in this guide does not qualify the cell for powered motion. Hardware bring-up and qualification are manual steps for the cell owner.
To try rt-core without hardware, use Run everything in simulation instead.
What you need#
- A cell host that matches a supported foundation profile (below), with its EtherCAT NIC wired to the drives.
- A live machine config for this cell (
"backend": "live") that compiles cleanly. See Configuration files. - A deployment pair id and a positive pair revision for this cell.
- A build machine of the same CPU architecture as the host, with the toolchain, to build the package.
- Root on the host, and network access from it to the IgH EtherCAT source archive during provisioning.
1. Provision the EtherCAT foundation#
host/ethercat-foundation.sh installs the IgH EtherCAT master for the running kernel, pinned by version and archive SHA-256, and configures its device access. It accepts only these host profiles:
| Profile | OS | Kernel | IgH |
|---|---|---|---|
revpi-debian12 | Debian 12 | *-revpi*-rpi-v8 | 1.6.9 |
rpi5-debian13 | Debian 13 | 6.18.*-rpi-v8-rt | 1.6.12 |
Any other host is refused. On other hardware you provision PREEMPT_RT and IgH yourself, and rtctl hostcheck (step 5) checks the result.
The script needs these variables. It refuses to run without NO_REAL_MOVEMENT=1.
| Variable | Description |
|---|---|
NO_REAL_MOVEMENT | Must be 1. |
ROSIE_RT_FOUNDATION_ETHERCAT_INTERFACE | The EtherCAT NIC's interface name. |
ROSIE_RT_FOUNDATION_ETHERCAT_MAC, …_ETHERCAT_PERMANENT_MAC | Its current and permanent MAC. |
ROSIE_RT_FOUNDATION_UPLINK_INTERFACE, …_UPLINK_MAC, …_UPLINK_PERMANENT_MAC | The host's other (uplink) NIC, so the two are never confused. |
ROSIE_RT_FOUNDATION_EXPECTED_SLAVES | Number of EtherCAT slaves expected on the bus. |
ROSIE_RT_FOUNDATION_RECEIPT_PATH | Where to write the foundation receipt. |
ROSIE_RT_FOUNDATION_PROFILE | Optional. Must match the detected profile. |
ROSIE_RT_ETHERCAT_GROUP | Optional. Group given access to /dev/EtherCAT0. Default ethercat. |
Run it in three stages: inspect changes nothing, install provisions, verify checks the result and runs rtctl hostcheck.
export NO_REAL_MOVEMENT=1 \
ROSIE_RT_FOUNDATION_ETHERCAT_INTERFACE=eth1 \
ROSIE_RT_FOUNDATION_ETHERCAT_MAC=<mac> ROSIE_RT_FOUNDATION_ETHERCAT_PERMANENT_MAC=<mac> \
ROSIE_RT_FOUNDATION_UPLINK_INTERFACE=eth0 \
ROSIE_RT_FOUNDATION_UPLINK_MAC=<mac> ROSIE_RT_FOUNDATION_UPLINK_PERMANENT_MAC=<mac> \
ROSIE_RT_FOUNDATION_EXPECTED_SLAVES=9 \
ROSIE_RT_FOUNDATION_RECEIPT_PATH=/etc/rosie-rt-core/ethercat-foundation.receipt
sudo -E bash rt-core/host/ethercat-foundation.sh inspect
sudo -E bash rt-core/host/ethercat-foundation.sh install
sudo -E bash rt-core/host/ethercat-foundation.sh verify
# ethercat_foundation_status=verified profile=… kernel=… expected_slaves=9install installs build dependencies with apt-get, downloads and checks the IgH archive, builds and installs the master and its kernel module, writes /etc/ethercat.conf, keeps NetworkManager off the EtherCAT NIC, adds a udev rule for /dev/EtherCAT0, and enables and restarts ethercat.service. It writes the receipt last. Running it again on a host whose receipt matches does nothing.
2. Build a runtime package#
On the build machine, build the live daemon against the pinned IgH userspace library, then stage a package:
cd rt-core
bash tools/build-igh-userlib.sh # 1.6.9 by default; pass 1.6.12 for rpi5-debian13
export PKG_CONFIG_PATH="$PWD/build/deps/igh-prefix/lib/pkgconfig"
make live control
bash tools/package-runtime.sh /tmp/rt-package
# package status=staged backend=live
# component status=staged output=/tmp/rt-package.components
bash tools/verify-package.sh /tmp/rt-packageThe package holds bin/ (rosie-rt-core, rt-control, rtctl, rt-package, and rt-natspublisher if built), lib/libethercat.so.1 with its licences, host/ (the install scripts and the three unit files), config/, every robot description's registered files, and rt_package.json, the manifest that verify-package.sh checks. Releasing describes the package and its companion archive.
The unit files in the package point at /opt/rosie-rt-core/current. Set ROSIE_RT_PACKAGE_SLOT_ROOT before packaging to use another absolute path (not under /home, /root or /run/user).
Copy /tmp/rt-package to the host.
3. Install the package, inactive#
sudo bash /tmp/rt-package/host/install.sh --from-package /tmp/rt-package --live
# Installed. No units were started.This:
- verifies the package, and with
--liverefuses a simulation package - creates the groups
rosie-rt,rosie-ctl,rosie-rt-clientsand the device group, and the system usersrosie-rt(the core) androsie-ctl(the adapter), with no home and no login shell - copies the package to
/opt/rosie-rt-core/<git-sha>/and points/opt/rosie-rt-core/currentat it, keeping the old target asprevious - installs
rosie-rt-core.service,rosie-rt-control.serviceandrosie-rt-natspublisher.servicein/etc/systemd/system/, and runssystemctl daemon-reload
It refuses to replace a stored release with different bytes under the same git identity. Nothing is enabled or started, and there is no deployment binding yet, so the units cannot start: each has ConditionPathExists=/etc/rosie-rt-core/control.env.
To configure the optional NATS observer, set ROSIE_RT_NATS_URL, ROSIE_RT_NATS_HOST and ROSIE_RT_NATS_CADENCE when you run install.sh. It then writes /etc/rosie-rt-core/natspublisher.env.
4. Set up remote access (optional)#
Skip this if every client runs on the cell host and uses the local socket. For remote clients, host/remote-pki.sh creates one CA per deployment pair. Run it after step 3, because it needs the rosie-ctl group. Keep the PKI directory outside the repository.
export ROSIE_RT_REMOTE_SERVER_SAN="DNS:rosie.local,DNS:localhost,IP:127.0.0.1"
sudo -E bash /opt/rosie-rt-core/current/host/remote-pki.sh init /var/lib/rosie-rt-pki/cell-a cell-a
sudo bash /opt/rosie-rt-core/current/host/remote-pki.sh issue-client /var/lib/rosie-rt-pki/cell-a pendant-1The identity given to init is the pair id and must match step 5. issue-client writes clients/<principal>.pem and clients/<principal>-key.pem; hand those and ca.pem to that client. Certificate details, SAN identity and revocation are in Remote access (mTLS).
Warning
The CRL expires after 7 days. Every init, issue-client and revoke regenerates crl.pem with a 7-day validity, and remote-pki.sh has no refresh command. rt-control checks the CRL on every handshake and refuses all remote connections once it has expired (remote CRL not current). Worse, if the CRL has already expired when rt-control starts, rt-control exits at startup, which also takes the local control socket down. Reissue the CRL before it expires, as below.
To reissue the CRL, run the same command the script ends with, in the PKI directory, and publish the result where rt-control reads it:
cd /var/lib/rosie-rt-pki/cell-a
sudo openssl ca -batch -config ca.cnf -gencrl -out crl.pem
sudo install -m 0640 -o root -g rosie-ctl crl.pem /etc/rosie-rt-core/pki/crl.pemrt-control reloads a changed CRL before each full handshake, so no restart is needed. Run this on a schedule shorter than 7 days.
5. Bind the deployment#
Put the machine config on the host (keeping a copy at /etc/rosie-rt-core/machine.json lets hostcheck find it later), then compile it into the installed binding:
sudo install -m 0644 machine.json /etc/rosie-rt-core/machine.json
sudo bash /opt/rosie-rt-core/current/host/generate-control-env.sh \
--remote-pki /var/lib/rosie-rt-pki/cell-a \
/etc/rosie-rt-core/machine.json cell-a 1
# configuration_sha256=<64 hex>Drop --remote-pki if you skipped step 4. The script runs rtctl control-env, which refuses any machine config that is not live. Only if compilation succeeds does it atomically replace:
/etc/rosie-rt-core/control.env(mode 0640, grouprosie-ctl), with the pair, the configuration digest, the core's arguments andROSIE_RT_REMOTE_ARGS/etc/rosie-rt-core/compiled/<digest>/axes.confandargv.json(grouprosie-rt)- with
--remote-pki,/etc/rosie-rt-core/pki/: the CA certificate, server certificate and key, and CRL readable byrosie-ctl, and the CA key and database readable by root only
The remote listener address comes from ROSIE_RT_REMOTE_LISTEN and defaults to 127.0.0.1:8443, loopback only. Set it to a host address to accept remote clients.
Record the printed digest. Put it in the cell config's nodes[].rt_core.configuration_sha256, and use it in every client's acquire binding. See Cells, machines and positioners.
Danger
At this revision the installer copies only axes.conf and argv.json into the compiled directory, and that directory is readable by the core's group only. control.env also points rt-control at it through ROSIE_RT_COMPILED_CONFIG, and rt-control reads resources.json and configuration.identity from there when it starts. Check that rosie-rt-control starts in step 7 before you rely on an installed cell.
6. Check the host#
sudo /opt/rosie-rt-core/current/bin/rtctl hostcheck --config /etc/rosie-rt-core/machine.json
sudo /opt/rosie-rt-core/current/bin/rtctl inventory --config /etc/rosie-rt-core/machine.jsonhostcheckis read-only. It checks PREEMPT_RT, the IgH master version, EtherCAT device permissions, CPU isolation andnohz_fullfor the RT CPU, the frequency governor, NIC IRQ affinity, timers, and the service users and directories. Exit 0 means every check passed; it never measures cycle latency.inventoryreads each slave's identity from the bus and compares it with the compiled configuration. Exit 0 means every slave matched.
If you have an owner-supplied host identity manifest, add --expected-release <file> to hostcheck to compare the installed units and loaded executables with it once the services are running.
7. Start the services#
When the cell is clear and the hardware E-stop is tested:
sudo systemctl enable rosie-rt-core.service rosie-rt-control.service
sudo systemctl start rosie-rt-control.service # also starts rosie-rt-core
systemctl status rosie-rt-core rosie-rt-controlThe core runs as rosie-rt with only CAP_SYS_NICE and CAP_IPC_LOCK, the adapter as rosie-ctl with no capabilities. The public sockets are /run/rosie-rt-core/control.sock and /run/rosie-rt-core/jog.sock (symlinks into public/), group rosie-rt-clients. Add the account that runs your clients to that group:
sudo usermod -aG rosie-rt-clients "$USER" # log in again afterwards
/opt/rosie-rt-core/current/bin/rtctl describeDescribe should report the ethercat backend and the digest from step 5. Starting the services does not arm or move anything: a client must acquire the lease, Home and arm first. The NATS observer, if configured, is rosie-rt-natspublisher.service; it has no command authority.
Update and roll back#
To update, build and install a new package (steps 2 and 3). The new release becomes current and the old one previous. Rerun step 5 if the machine config changed, then restart the units.
To go back one release:
sudo bash /opt/rosie-rt-core/current/host/install.sh --rollback
# Rolled back current to <git-sha>. No units were started.
sudo systemctl restart rosie-rt-control.serviceRollback verifies both stored releases and swaps current and previous. It works once: a second rollback is refused until you install another package. Clients must acquire again after the restart.
Installing from a source tree#
host/install.sh run from a checkout, without --from-package, installs the binaries from rt-core/build/ into /opt/rosie/rt-core/ instead, and with machine.json pair-id pair-revision arguments also generates the binding (and --enable enables the units). This form exists for the repository's own tests. Use a package for a cell.