Generic hardware contracts and safe device-control foundations for Blacknode.
This package provides the stable boundary between Blacknode workflows and
physical devices. Workflows use capabilities such as mobile_base, camera,
range_sensor, battery, and servo_bus; replaceable adapters implement
those capabilities for a particular transport or device.
The initial foundation is intentionally hardware-independent:
- capability and device-state contracts
- disarmed-by-default motion authorization
- command freshness and velocity-limit validation
- a configurable I2C mecanum-base adapter
- a generic joint-group contract for real actuator adapters
- an editor node for capability inspection
The next implementation layer can add adapters for USB, serial, I²C, GPIO, CAN, ROS 2, or a network hardware service without changing workflow names.
From the Blacknode repository root:
blacknode packages install .\packages\blacknode-hardwareOr install the package checkout directly during development:
pip install -e .\packages\blacknode-hardwareNo device SDK is required for package discovery or contract inspection.
For a guided Ubuntu setup:
./setup_ubuntu.sh
./check.sh --software-onlyThe setup script installs packages and creates .venv. It reports the boot
configuration needed for I2C but does not silently rewrite system files.
Discover connected serial devices:
./discover.shFor any number of connected serial robots, use the automatic setup:
./configure.sh --all --installBlacknode deduplicates /dev/serial/by-id/... and /dev/tty... aliases,
checks every distinct serial adapter with position reads, and keeps only buses
where servos respond. It detects servo IDs 1 through 20 by default. Increase
the read-only scan range when a robot uses higher IDs:
./configure.sh --all --install --servos 40The command shows each stable serial path and detected servo IDs, then asks for
a friendly name such as Left arm or Packing arm. Names remain attached to
the same serial hardware on later scans. For unattended setup, provide names in
discovery order:
./configure.sh --all --install \
--name "Left arm" \
--name "Packing arm" \
--no-promptEach robot receives its own stable device identity, configuration directory,
pairing token, active-calibration file, systemd service, and available HTTP
port starting at 8765. Port 8766 remains reserved for the Blacknode runtime,
so the first robot services use 8765, 8767, 8768, and subsequent ports.
The final pairing checklist prints each friendly name beside its URL
and private token so the robots can be added to the Blacknode editor one at a
time. The discovery probe does not enable torque, write goal positions, or move
a robot.
Inspect or operate every configured robot later:
./configure.sh --all --show
./pair.sh --all --show
./service.sh --all status
./service.sh --all restart
./service.sh --all checkSingle-robot configuration remains available:
./configure.sh \
--name "Left arm" \
--servos 6 \
--port /dev/serial/by-id/DEVICE_PATH
./pair.sh
./install-service.shPerform the first real hardware check with a read-only actuator probe:
./probe.sh --servos 6--servos 6 scans serial IDs 1 through 6. Without --servos, the probe
scans IDs 1 through 20. It reads present positions only and does not enable
torque, write goal positions, or move the arm. Override the connection
defaults when required:
BLACKNODE_SERIAL_PORT=/dev/ttyACM0 \
BLACKNODE_SERIAL_BAUDRATE=1000000 \
./probe.sh --servos 6Save the detected serial hardware for the device service:
./configure.sh --servos 6The configuration is saved atomically to:
.blacknode-hardware/device.json
This directory is ignored by Git. Running configure.sh again updates the
same configuration, so the device can be reconfigured at any time:
./configure.sh --show
./configure.sh --servos 7
./configure.sh --port auto
./configure.sh --port /dev/serial/by-id/DEVICE_PATH --baudrate 1000000
./configure.sh --device-id arm-01Settings not included in a reconfiguration command are preserved. Configuration and status monitoring are read-only and never enable torque or send goal positions. If the persistent service is already installed, apply configuration changes with:
./service.sh restartCreate a private device token before installing the persistent service:
./pair.shThe token is stored at .blacknode-hardware/auth.token, outside Git, with
owner-only file permissions. Save the displayed token for the Blacknode editor
device connection. Pairing can be inspected or replaced later:
./pair.sh --show
./pair.sh --rotateRotation immediately invalidates the previous token and restarts an active
hardware service. /health remains public for reachability checks.
/status, /capabilities, and /rpc require:
Authorization: Bearer PAIRING_TOKEN
python -m pytest packages\blacknode-hardware\testsThe included I2C adapter is the first physical hardware path; it requires
smbus2 and an explicit device profile. Additional actuator and sensor
protocols belong in separate adapters and must implement the same contracts.
Serial device discovery uses pyserial and is installed with the package.
For a foreground test that stops when the terminal closes:
./start.shPress Ctrl+C before installing the persistent service.
Install and start the systemd service:
./pair.sh
./install-service.shThe installer:
- validates
.blacknode-hardware/device.json - validates the private pairing token
- generates the service with the current repository path and Linux user
- enables automatic startup after reboot
- restarts the service after a failure
- allows the assigned TCP port when UFW is active
- waits for the HTTP service and connected hardware to pass validation
Re-running install-service.sh safely updates the existing service definition.
Use the service manager afterward:
./service.sh status
./service.sh check --require-hardware
./service.sh restart
./service.sh logs
./service.sh follow
./service.sh stop
./service.sh startstatus shows systemd state and performs a live HTTP check. logs shows the
latest 100 journal entries; follow streams new entries until Ctrl+C.
The service uses:
host: 0.0.0.0
port: 8765
Automatic multi-robot setup assigns 8765, skips the runtime service at
8766, and then assigns 8767 and subsequent ports in stable device order.
Each service status also reports its exact stable serial path. Remote
deployment uses that path to bind a one-robot workflow to the paired robot
instead of falling back to USB discovery index 0.
The authenticated release and resume RPC methods transfer that serial port
between the read-only device monitor and the deployed robot driver. Releasing
one robot does not stop the other configured robot services.
When UFW is active, install-service.sh adds an exact TCP
allow rule for each assigned robot port. It does not open unused ports. Set
BLACKNODE_CONFIGURE_UFW=0 to manage firewall rules separately:
BLACKNODE_CONFIGURE_UFW=0 ./configure.sh --all --installFor the split leader/follower templates on separate computers, opt in to the leader rosbridge rule as part of setup:
./configure.sh --all --install --teleopWhen UFW is active, --teleop also permits TCP 9091. Rosbridge does not use
the hardware pairing token, so use this option only on a trusted robot LAN and
restrict the generated rule to the follower host or trusted subnet when your
network requires tighter access.
When automatic firewall configuration is disabled, allow the selected port manually:
sudo ufw allow 8765/tcp
sudo ufw reloadFrom your PC, the public health URL can be opened directly:
http://PI_IP_ADDRESS:8765/health
For example:
http://192.168.1.87:8765/health
Use the pairing token for protected endpoints. From PowerShell:
$token = Read-Host "Pairing token"
$headers = @{ Authorization = "Bearer $token" }
Invoke-RestMethod http://PI_IP_ADDRESS:8765/status -Headers $headers
Invoke-RestMethod http://PI_IP_ADDRESS:8765/capabilities -Headers $headers/status refreshes all configured servo positions on every request and
reports both raw ticks and nominal degree values. It also reports the active
calibration's name, profile, hardware identity, joint count, and digest.
/health advertises calibration_activation in features when this API is
available.
Calibration activation is an explicit, authenticated operation. In the
Blacknode editor, select the paired device under Deployments, choose a
saved calibration, and press Use this calibration. The service accepts the
profile and calibration only while the device is connected and disarmed, and
only when their servo IDs exactly match the configured bus topology. The
activation is stored in active-calibration.json beside the device
configuration and remains active across service restarts. A changed device ID
or bus topology invalidates the stored activation.
An editor message saying that calibration activation is unsupported means the device service is older than this API. Update this checkout on the device and restart it:
./service.sh restartThe service remains read-only: calibration activation records the identity and safety contract used for deployment preflight, but it cannot arm or move the servos.
To verify the firewall and listener on the Pi:
sudo ufw status
ss -ltnp | grep 8765From Windows PowerShell:
Test-NetConnection PI_IP_ADDRESS -Port 8765The health endpoint can work while status reports "connected": false; that
means the service is reachable but one or more configured servos did not
respond. Run ./probe.sh --servos 6 to check the serial connection directly.
Pairing authenticates access but plain HTTP does not encrypt the token or traffic. Keep the service on a trusted LAN. Use a VPN or HTTPS before internet exposure, deployment uploads, or motion control.