English | 简体中文
A compact handheld interface for a six-axis RobStride robot arm, built on Zephyr RTOS. Monitor joint telemetry, control the arm with a rotary encoder, teach motions by hand, and manage saved trajectories from a 170 × 320 color display.
Physical prototype; the photo shows an earlier input-test screen.
Watch the hardware demo on Bilibili: MONITOR telemetry, MASTER local control, and TEACH trajectory recording, replay, and Flash storage on reBot Arm RS.
All UI images below are generated by the firmware renderer using illustrative data; they are not hardware captures or validation results.
Discover J1–J6 on CAN and display position, velocity, torque, temperature, and faults. MONITOR and MASTER share joint lists and detail views.
Joint list and J2 telemetry detail.
Adjust joint positions with the encoder, enable or disable individual joints or the whole arm, set mechanical zero, and return smoothly to zero. Damping and impedance modes include gravity compensation for the configured arm model.
MASTER joint list and encoder position control detail.
Firmware renderer preview: MASTER function entries below the joint list (left), and the menu scrolled to the final action (right).
With the RobStride arm profile, after the bus-idle check succeeds, entering MASTER selects ALL JOINTS and automatically requests ENABLE for J1–J6 to hold their current positions. The full-screen result confirms each joint; failure or partial completion does not retry automatically. You can then use ENABLE / DISABLE, or select an individual joint. TEACH uses the same automatic enable-all flow after successful entry. POSITION, DAMP + GRAV, and IMP + GRAV select position, damping, and impedance control; + GRAV indicates gravity compensation. RETURN ZERO plans a smooth move to the existing zero, while SET ZERO changes the mechanical zero reference. Use the joystick up/down to select rows and press to confirm.
The separate DM4340P single-motor bench MASTER profile supports J1 enable/disable with feedback confirmation, actual-position hold and encoder jog. Its menu contains only configured joints and supported actions. It uses independent low-gain bench settings; homing, zeroing, gravity compensation and TEACH are not available. See Damiao configuration and commissioning. A separate experimental DM position/velocity profile uses the drive’s own position loop with 0.1 rad encoder steps and direct targets. It reads the drive’s MAX_SPD/Gr output-speed ceiling instead of applying an additional HMI speed cap; mode switching requires verified disabled feedback. Position-mode travel uses the verified PMAX range (currently ±12.5 rad), without a session-relative trial window. See the same guide for its separate build and commissioning procedure.
Record up to three minutes of six-axis motion, with dedicated REC/PLAY screens, slow alignment to the start, and original-speed playback.
Dedicated recording and original-speed replay screens.
After recording, LATER is selected by default; choose SAVE to open the trajectory library. After loading, choose REPLAY or LATER; motion still requires explicit confirmation and the existing controller checks.
16 persistent slots with memorable names, save, load, overwrite, and delete. Alternating Flash copies and CRC checks protect commits; loading a path does not start motion.
Named trajectory slots and slot actions.
Request position, damping, or impedance controller switching through MONITOR → ROS MODE, with host verification and rejection while a trajectory is active.
Controller selection and the active-trajectory interlock.
CAN health, feedback age, control timing, fault history, temperature alerts, and guided USB DFU upgrades with backup and byte-for-byte verification.
CAN health and control/display timing diagnostics.
Getting started · Teach and storage · ROS MODE protocol · Documentation
| Component | Configuration |
|---|---|
| MCU | STM32G474CCT6; 256 KiB internal Flash, 128 KiB application RAM region |
| Display | ST7789, 170 × 320, SPI with DMA and partial updates |
| Input | ALPS rotary encoder with directional and center presses |
| CAN | 1 Mbit/s classic CAN; RobStride motor protocol and a separate HMI/PC protocol |
| External storage | W25Q64JV, 8 MiB SPI NOR; fitted part verified by JEDEC ID and SFDP |
| Board assembly | Processor/display board connected to a separate power/CAN board |
| Development | Pinned Zephyr dependencies and a Docker-based SDK environment |
The external Flash budget reserves 2 MiB for trajectories, 256 KiB for parameters, 1 MiB for model/calibration/IK data, 4860 KiB for expansion, and 4 KiB for diagnostics. Compile-time boundaries prevent trajectory growth into reserved regions. Parameter services and an IK solver are not implemented yet; IK code, RAM workspace, and execution time require separate MCU resource planning. See storage layout.
Validation status: firmware startup, the trajectory-library menus, and basic external NOR checks have passed hardware checks. Both legacy slots remain visible after restart, and slot 01 passes full load validation. The loaded-path REPLAY/LATER prompt is confirmed. Slot 02 payload loading, new recording/save prompts, newly named trajectory persistence, and ROS MODE switching still need hardware integration validation. The bring-up log contains evidence and release history.
Use an ARM64 Linux host with Docker and access to the private development image. The repository pins the validated image digest in config/dev-image.env; installing the Zephyr SDK on the host is unnecessary.
git clone git@jihulab.com:robot_group/zephyr_embedding/arm_hmi_node.git
cd arm_hmi_node
docker login registry.jihulab.com
./scripts/init-workspace.sh
./scripts/build-hmi.sh --can--can builds the complete display, CAN, MASTER, and TEACH application. Available profiles:
| Command | Profile |
|---|---|
./scripts/build-hmi.sh --can |
Full HMI application |
./scripts/build-hmi.sh --display |
Display and menu testing without CAN control |
./scripts/build-hmi.sh |
Basic input bring-up |
The published image is ARM64. AMD64 hosts need a validated image for that architecture, selected through ZEPHYR_DEV_IMAGE. The environment repository is separate at ../zephyr_embedding_ws; this project's directory is bind-mounted into the container. ./scripts/dev.sh opens its development shell.
Exit MASTER/TEACH and connect the Type-C data cable with S1 in OFF/UART. An upgrade clears unsaved RAM trajectories.
./scripts/request-dfu.sh --ping-only
python3 scripts/flash.py --can --check # Inspect the existing BIN and USB; no reset or write
python3 scripts/flash.py --can # Build, back up, flash, and verifyOnce the script confirms the upgrade request, keep power connected, switch S1 to ON/DFU within eight seconds, and press Enter as prompted. The script verifies the target STM32, backs up its 256 KiB internal Flash, writes the firmware, and compares the readback byte for byte. It stops on backup or verification failure. Records and hashes are stored under .cache/flashes/.
After flashing, fully remove power from the HMI, set S1 back to OFF/UART, reconnect, and verify startup with --ping-only. For an existing DFU session and a prebuilt artifact:
python3 scripts/flash.py --from-dfu --firmware /absolute/path/zephyr.binThe defaults identify the current test board. For another unit, specify its verified STM32 serial number with --serial and UART port through SERIAL_DEVICE. Generic serial-terminal defaults can toggle this board's DTR/RTS boot lines; use the supplied scripts. Readback verification confirms the binary was written, while runtime validation remains a separate step.
- Stop the PC bridge and host CAN commands, then enter TEACH after the bus-idle check. Entry automatically enables J1–J6 and holds their current positions; wait for the verified result. Failed/partial enable does not retry automatically.
- With all joints enabled, select RECORD and confirm. Drag the arm while the REC screen is active; press again to stop and hold. After validation, the firmware gives the recording a short name and asks LATER / SAVE, with LATER selected by default.
- Choose SAVE, select a slot (the first empty slot is selected by default), and confirm. Replacing an occupied slot requires another confirmation. Wait for
SAVED AND VERIFIED. You can also save later through TRAJECTORIES → slot 01–16 → SAVE RAM. - To use a saved path, choose LOAD. The firmware checks its CRC, timestamps, and joint ranges, then asks REPLAY / LATER. LATER returns to TEACH with REPLAY selected.
- Enable all joints if needed and explicitly confirm REPLAY. The arm slowly aligns with the first point before playback at the recorded speed.
Menu order: RECORD → REPLAY → RETURN ZERO → ENABLE ALL → DISABLE ALL → TRAJECTORIES. Entry selects RECORD; recording and replay still require explicit confirmation. ENABLE ALL remains available for manual retries or after disabling.
Names such as BLUE FOX persist with the trajectory. Existing unnamed slots remain readable as SAVED 01–SAVED 16; no format or migration is required.
Recording feedback and replay references share per-joint speed limits: J1 4.0, J2/J3 3.0, J4–J6 5.0 rad/s (MoveIt configuration ×2). Replay tracking tolerance is 0.20 rad (physical position-controller path tolerance ×2). Stop dragging before finishing recording; see motion limits and scope.
New recording replaces only the RAM working path; unsaved recordings disappear on reboot. DELETE removes a saved slot while leaving an already loaded RAM copy available. Overwrite, load, and delete require confirmation. Recalibrating motor zero or changing the arm requires recording new paths.
Local MASTER/TEACH requires three seconds of bus silence and relinquishes control if another master appears. Exiting holds the current position and stops HMI transmissions. These controls do not replace a mechanical emergency stop or collision detection. See teaching, persistent storage, and control behavior.
The green LED indicates CAN reception or successful transmission, with an approximately 100 ms activity hold. It does not identify the firmware version or guarantee joint health. The white LED indicates power; use ABOUT or UART ping / status for firmware and system state.
DIAGNOSTICS shows bus health, feedback freshness, control timing, stack headroom, and recent faults. External NOR checks use:
python3 scripts/check-external-flash.py # Read-only identification
python3 scripts/check-external-flash.py --write-test # Full reads and blank-tail-sector testExit MASTER/TEACH before the write test. It uses the dedicated final 4 KiB sector and refuses to overwrite a nonblank test sector. See Flash diagnostics.
| Path | Purpose |
|---|---|
app/src/control/ |
Arm profile, control, trajectory recording and replay |
app/src/protocol/ |
Motor interface, feedback types, RobStride adapter and codecs |
app/src/ui/ |
UI state, backend adapters, shared components, and pages |
app/src/storage/ |
Trajectory format, Flash layout, and diagnostics core |
boards/others/arm_hmi_node/ |
Board pins, clocks, and memory configuration |
manifest/west.yml |
Pinned Zephyr and module dependencies |
scripts/ |
Workspace setup, container access, builds, and guided upgrades |
tests/ |
Host-side protocol, UI, controller, storage, and upgrade tests |
docs/ |
Operating guides, architecture, and hardware validation records |
assets/ |
Branding and README images |
deps/, modules/, .west/, build/, and .cache/ are local dependencies and artifacts excluded from Git. Shared components belong in the separate embedded_common repository, which currently remains a skeleton rather than a build dependency.
The home menu is MONITOR / MASTER / TEACH / DIAGNOSTICS / ABOUT. UI pacing is fixed at the original default (20 ms minimum work cycle, approximately 50 ms CAN polling wait); actual LCD updates depend on rendering and SPI transfer time.
The motor adapter boundary separates UI/control snapshots, robot gains/limits, and wire encoding. RobStride control, Damiao Classic CAN monitoring, and an explicit DM4340P single-motor bench MASTER profile are implemented. The bench profile supports verified enable/disable, hold and encoder jog. Damiao monitor profiles still reject local control; whole-arm dynamics and TEACH are not enabled. Existing saved-trajectory coordinates are preserved. See architecture and protocol extension. The measured DM4340P single-motor monitor profile uses command ID 1, feedback ID 0 and PMAX/VMAX/TMAX 12.5/20/28; J2–J6 are unused. See the read-only commissioning results and build command.
Run the Python test suite in the development environment:
./scripts/dev.sh python -m unittest discover -s tests -p 'test_*.py' -vCI also runs standalone C tests and builds the three standard firmware profiles plus Damiao monitor and single-motor MASTER profiles, retaining ELF/BIN/HEX files and hashes. It requires an ARM64 Docker executor with permission to pull the private image; deployment on an actual GitLab Runner is still pending. CI does not operate hardware. Storage power-cut simulations and controller timing simulations supplement hardware acceptance rather than replace it.
Detailed guides are currently primarily in Chinese.
- Architecture and UI navigation
- CAN integration, Damiao / DM4340P scope and configuration, and local MASTER
- Position control and compliance modes
- Teach and replay and persistent trajectory storage
- Display bring-up and external Flash diagnostics
- Hardware validation and release history
MONITOR → ROS MODE at the bottom of the joint list offers:
| HMI label | Mode value | Controllers activated on the host |
|---|---|---|
| POSITION | 1 | rebot_arm_rs_position_adapter + rebot_arm_rs_trajectory_controller |
| DAMPING | 2 | rebot_arm_rs_damping_controller |
| IMPEDANCE | 3 | rebot_arm_rs_impedance_controller + rebot_arm_rs_impedance_trajectory_controller |
Rotate or move up/down to select a mode. Press to open full-screen confirmation, then press again to send; left cancels confirmation. No other requests are accepted while waiting. SUCCESS appears only after the host queries and verifies the requested mode, and closes automatically after one second. Failure messages require a press to dismiss. PC ONLINE means only that the bridge heartbeat is fresh; ROS NOT READY means the switching conditions are not met. MONITOR joint rows remain read-only: this feature neither starts local MASTER nor directly sends motor enable, disable, or position commands.
The host implementation is in the separate rebot_arm_rs_ros2/rebot_arm_hmi_bridge repository package. It uses SocketCAN through the Python standard library, without a python-can dependency. Start the hardware and controllers through the existing host workflow, then start the bridge. The five switchable controllers must already be loaded and configured as active/inactive. The underlying rebot_arm_rs_mit_controller must be active, with exactly one complete mode combination active.
In a host-container terminal configured for ROS 2 Jazzy:
cd /workspaces/zephyr-dev/zephyr_ws
source /opt/ros/jazzy/setup.bash
colcon build --packages-select rebot_arm_hmi_bridge --symlink-install
source install/setup.bash
ros2 run rebot_arm_hmi_bridge hmi_bridge --ros-args \
-p can_interface:=can0 \
-p controller_manager:=/controller_manager \
-p controller_namespace:=/controller_namespace is the namespace of controller nodes, excluding the controller name; the default root namespace is /. The container must have access to the existing SocketCAN interface and ROS DDS domain. This node does not change the can0 bitrate, start/stop host hardware, or automatically load controllers. Run only one bridge per CAN bus. A process lock covers the same user/interface; deployment must enforce a single instance across users or containers.
If a trajectory is running, the HMI displays STOP TRAJECTORY FIRST; the bridge does not cancel it. Use the FollowJointTrajectory action interfaces of the two trajectory controllers. ACCEPTED, EXECUTING, and CANCELING all count as busy. The active JTC's state must also be fresh, within 0.5 seconds. Reference and measured velocities for all six axes must remain below 0.01 rad/s for at least 0.5 seconds, and the action-status publisher must have matched for at least one second. Trajectories sent directly to the joint_trajectory topic lack a complete queryable lifecycle. If any publisher exists on that topic, the bridge conservatively refuses switching with ROS NOT READY; stop that publisher and use the action interface. The node subscribes to both action-status streams but never calls cancel_goal.
Concurrency boundary: checking trajectory status and calling switch_controller are not atomic ROS operations. Other clients must pause new goals and controller-switch requests during switching. This version does not provide a global lock across ROS clients. It rejects known active trajectories but cannot guarantee freedom from races with arbitrary simultaneous new goals.
Local MASTER/TEACH and the PC bridge are mutually exclusive. Bridge heartbeats count as CAN activity, so local MASTER's three-second idle condition cannot be met while the bridge runs. To use local HMI MASTER/TEACH, stop both the PC bridge and the host's CAN commands before entering through the normal workflow. ROS MODE does not require three seconds of silence.
This project-specific protocol uses the existing 1 Mbit/s classic CAN bus, 11-bit standard frames, and DLC=8. Extended frames, RTR, CAN FD, and other lengths are rejected. The three IDs are reserved for HMI/PC application communication, not RobStride motor commands. RobStride's 29-bit extended-frame protocol remains unchanged.
| CAN ID | Direction | Meaning |
|---|---|---|
0x6F0 |
HMI → PC | Mode-switch REQUEST |
0x6F1 |
PC → HMI | Request acceptance / final RESULT |
0x6F2 |
PC → HMI | STATE heartbeat, one frame every 200 ms |
The first six bytes of every frame are: byte0=protocol version 1, byte1=mode, byte2..5=nonzero 32-bit token, little-endian.
| Frame | byte1 | byte6 | byte7 |
|---|---|---|---|
| REQUEST | Requested mode 1/2/3 | Reserved; must be 0 | Reserved; must be 0 |
| RESULT | Requested mode for this transaction | Result code, listed below | Newly observed mode 0/1/2/3 |
| STATE | Observed mode; 0=unknown | State bits: bit0 READY, bit1 BUSY, bit2 TRAJECTORY, bit3 FAULT | Capability bits: bit0 POSITION, bit1 DAMPING, bit2 IMPEDANCE |
Other STATE bits must be zero. READY means the state, service, and trajectory conditions are met. BUSY means a request is being processed or a timed-out service is still pending. FAULT means the controller combination is unknown or its observation has expired. The HMI sends a request only when the heartbeat is no older than 750 ms, READY=1, BUSY/TRAJECTORY/FAULT=0, and the requested capability bit is present. The actual mode is determined from the controller list, not inferred from the last request.
| Result code | Name | HMI message / meaning |
|---|---|---|
| 0 | ACCEPTED | WAIT FOR ROS; accepted, not yet successful |
| 1 | DONE | MODE VERIFIED; actual controller combination verified |
| 2 | BAD_REQUEST | INVALID REQUEST; the same token carried a different mode |
| 3 | STALE_TOKEN | REFRESH AND RETRY; the request token expired or does not match |
| 4 | NOT_READY | ROS NOT READY; services, observations, or target controllers are not ready |
| 5 | TRAJECTORY_BUSY | STOP TRAJECTORY FIRST; stop the trajectory first |
| 6 | SWITCH_FAILED | ROS SWITCH FAILED; service failure or exception |
| 7 | VERIFY_FAILED | MODE NOT VERIFIED; the resulting mode could not be verified |
| 8 | SERVICE_TIMEOUT | ROS SERVICE TIMEOUT; the service timed out and may still take effect later |
| 9 | REQUEST_BUSY | ROS BUSY; another request or timed-out service is still pending |
| 10 | UNKNOWN_CONFIG | CHECK CONTROLLERS; the configuration is not a known complete mode |
Malformed requests, unsupported versions, invalid modes, nonzero reserved bytes, or token=0 are ignored without calling a ROS service. Additional local HMI messages—PC OFFLINE, TX FAILED CHECK MODE, TIMEOUT CHECK ROS MODE, and LOCAL CONTROL BUSY—do not use on-wire result codes.
The PC generates a random nonzero token at startup. An idle token has a five-second lease; the token increments on expiry or transaction completion, skipping zero. The HMI echoes the token from the latest STATE unchanged rather than generating its own sequence number. On the first matching request, the PC consumes the token, replies ACCEPTED, rechecks the latest controller and trajectory state, switches the known controller combination using STRICT switch_controller, and calls list_controllers again to verify the result before replying DONE or failure.
Only active controllers belonging to the current mode are deactivated, and only inactive controllers belonging to the target mode are activated. Adapters activate from downstream to upstream; the old chain deactivates in reverse order. The underlying MIT controller, joint_state_broadcaster, and gripper remain unchanged. An unknown controller claiming the six axes' or known chains' command interfaces causes rejection. If the requested mode is already active, the bridge verifies the state without calling the switching service again.
Repeated (token, mode) requests return cached acceptance/results without switching again. A different mode with the same token returns BAD_REQUEST. The cache holds at most 64 entries for up to 60 seconds; older requests are rejected as STALE_TOKEN. A bridge restart generates a new random initial token. Tokens prevent accidental replay and correlate results; they are not authentication credentials or security keys. Deployment must ensure that nodes on the shared CAN bus are trusted.
The list_controllers client deadline is 600 ms. A switch request sets timeout=2 seconds, while the client waits at most 2.5 seconds. The HMI's overall deadline is 4.5 seconds. The HMI reports success only if the request token and target mode match and a DONE result reports the requested actual mode. ACCEPTED or a CAN link ACK does not mean the switch succeeded. A late result does not overwrite a timeout already shown to the user; heartbeats continue to display the actual mode so the user can inspect it before requesting another switch.
Timeouts do not trigger automatic retries, trajectory cancellation, rollback, or motor enable/disable. A ROS service timeout also does not cancel its execution. The bridge retains the unfinished future and blocks new switches; after its final response arrives, it queries the controllers again. If the service never finishes, inspect controller_manager and restart the bridge manually. Partial switching or an unknown controller combination leaves FAULT set until manual recovery.
The HMI normally remains in LISTENONLY. A confirmed request temporarily switches its CAN controller to NORMAL, sends one standard REQUEST frame, and waits for a result. CAN transmit confirmation is bounded to 20 ms; on failure, the controller is stopped to terminate hardware retransmissions. The HMI returns to LISTENONLY after the final result or overall timeout. While waiting, it still receives motor telemetry but does not acquire local MASTER ownership; the command gate blocks DFU and local MASTER requests. Losing the PC heartbeat does not change the host controllers' running mode.
Suppose the latest STATE supplies token 0x78563412, the current mode is POSITION, and all three modes are supported. A switch to DAMPING looks like this:
6F2#0101123456780107 PC heartbeat: POSITION, READY, all three modes supported
6F0#0102123456780000 HMI request: DAMPING
6F1#0102123456780001 PC accepted: still POSITION
6F1#0102123456780102 PC completed: actual mode is DAMPING
6F2#0102133456780107 Next heartbeat: DAMPING, new token 0x78563413
These are encoding examples. A fixed token is not a permanently valid cansend command. Observe traffic with these read-only commands:
candump can0,6F0:C00007FF,6F1:C00007FF,6F2:C00007FF
ros2 control list_controllersThe HMI UART status command includes ROS HOST online=... mode=... flags=... pending=... result=... ticket=.... It is read-only and sends no CAN commands; retain this board's safe DTR/RTS settings. For PC OFFLINE, first check the bridge, interface, and three frame types. For ROS NOT READY, check the underlying MIT controller and active chain, JTC state, direct topic publishers, and services. After a result timeout, query the actual mode before deciding the next step. Hardware integration testing should cover all three mode switches, rejection during an active trajectory, and the offline indication after disconnecting the bridge, while confirming that local HMI MASTER was never activated.









