Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Installation

nano-ros is distributed as source, vendored into the consumer’s project tree (git submodule, west manifest, ESP-IDF component, etc.) and built in-tree via add_subdirectory(nano-ros). There is no binary tarball, no system-wide install step, no find_package(NanoRos), and no prebuilt nros CLI.

Host prerequisites (Ubuntu/Debian)

Everything toolchain-shaped — rustup, cross-compilers, emulators, the zenoh router — is provisioned by the steps below. The host itself only needs the basics:

sudo apt-get update
sudo apt-get install -y git curl ca-certificates build-essential \
    pkg-config python3

The whole flow, end to end

Four steps take a bare machine to a building project; each is detailed in a section below.

# 1. Pull the source at a pinned version (or `main` for latest):
git clone --branch nros-v0.5.0 https://github.com/NEWSLabNTU/nano-ros.git
cd nano-ros

# 2. Build the `nros` CLI from source (installs rustup if needed):
./scripts/bootstrap.sh

# 3. Wire the workspace env into this (and every future) shell:
source ./activate.sh          # or: direnv allow / source ./activate.fish

# 4. Provision the toolchains + RMW daemon for your target:
nros setup <board> --rmw <zenoh|xrce|cyclonedds>    # e.g. `native`

Then point your project’s manifest at the checkout — Pattern A/B below for CMake (the add_subdirectory / -DNANO_ROS_ROOT guard), or the Rust-only consumers path dependency. See Pinning a version for how the tag relates to nros version.

activate.sh is the after-install step, NOT a prereq. Sourcing activate.sh (or activate.fish, or direnv allow) only wires the workspace env into a new shell — it presumes the nros binary already exists at packages/cli/target/release/nros. The bootstrap front door below is what produces that binary; the activate file is what makes it findable in every subsequent shell.

See build-as-subdirectory.md for the canonical user incantation (4-line CMakeLists.txt). This page walks the surrounding workspace + per-target setup choices.

Pattern A: nano-ros lives inside your ROS 2 workspace

The recommended layout is to clone nano-ros into your workspace’s src/ directory alongside your packages:

~/ros2_ws/
├── src/
│   ├── nano-ros/             # <-- this repo
│   ├── pkg_a/                # your package(s)
│   ├── pkg_b/
│   └── …
└── (build/, install/, log/ — generated by colcon)

colcon build discovers nano-ros + each user package via package.xml, builds them in dependency order, and shares one nano-ros build across every consuming package. Users never run cmake manually — the per-user package’s CMakeLists.txt does add_subdirectory(../nano-ros nano_ros) under the hood.

A complete working example lives at examples/templates/multi-package-workspace/.

Pattern B: nano-ros as a third-party subdirectory

For C/C++ projects that don’t use colcon at all:

~/my_project/
├── CMakeLists.txt
├── src/main.c
└── third_party/
    └── nano-ros/             # git submodule

The top-level CMakeLists.txt:

cmake_minimum_required(VERSION 3.22)
project(my_app LANGUAGES C)

# `NROS_RMW` is the user-facing cache var (overridable via
# `-DNROS_RMW=<rmw>`); forward it to `NANO_ROS_RMW`, the var the
# nano-ros add_subdirectory reads. Matches the canonical example
# shape in examples/native/c/talker/CMakeLists.txt.
set(NANO_ROS_PLATFORM posix)
set(NROS_RMW "zenoh" CACHE STRING
    "Active RMW (zenoh|xrce|cyclonedds) — selects the backend linked into my_app.")
set(NANO_ROS_RMW "${NROS_RMW}")
add_subdirectory(third_party/nano-ros nano_ros)

add_executable(my_app src/main.c)
target_link_libraries(my_app PRIVATE NanoRos::NanoRos)
nros_platform_link_app(my_app)

nros_platform_link_app transitively wires the selected RMW backend on POSIX — no explicit nano_ros_link_rmw() call is needed. That is the entire consumption shape. See build-as-subdirectory.md for the full walkthrough.

Provision your toolchain with nros setup

nros setup is the single command that prepares a machine to build nano-ros for a given board. It ships prebuilt toolchains per platform per RMW — the cross-compiler, emulator, RMW host daemon, and any SDK sources a board needs are fetched from a pinned index and placed in a shared store (~/.nros/sdk). You do not install cross-toolchains by hand, and you do not need ROS 2 on the machine.

1. Get the nros CLI onto PATH

nano-ros is a source distribution — there is no prebuilt nros download. One front door from a fresh checkout (just is NOT a prereq; rustup is installed on demand):

git clone https://github.com/NEWSLabNTU/nano-ros.git
cd nano-ros
./scripts/bootstrap.sh

Builds the in-tree nros CLI from source at packages/cli/target/release/nros and leaves it on PATH for this shell.

Already have cargo? The equivalent by hand (same build, same binary):

git submodule update --init packages/cli/third-party/ros-launch-manifest
cargo build --release --manifest-path packages/cli/Cargo.toml --bin nros
export PATH="$PWD/packages/cli/target/release:$PATH"

Both produce the per-checkout binary at packages/cli/target/release/nros. One checkout = one CLI version = one runtime ABI — no global install, no ~/.nros/bin PATH skew across worktrees.

2. Activate the workspace (every subsequent shell)

The bootstrap path you ran in §1 left nros on PATH for that shell only. Every new shell needs the workspace env wired in. Pick whichever fits your shell — all three wire the same env exports + PATH entries from the Phase 218.C SSoT activate.sh:

direnv allow                  # auto-activates on `cd nano-ros` (recommended)
source ./activate.sh          # bash / zsh, one-shot per shell
source ./activate.fish        # fish, one-shot per shell

The activate file also sources /opt/ros/humble/setup.bash if present (required by nros generate-rust + cyclonedds codegen + rmw_zenoh interop tests) and exports NROS_REPO_DIR.

3. Provision a board (+ RMW)

nros setup <board> --rmw <zenoh|xrce|cyclonedds>

nros setup resolves the board’s package set union the RMW’s host packages and fetches them all — prebuilt cross-toolchain + emulator + RMW daemon + board SDK sources. --rmw defaults to zenoh.

CommandProvisions
nros setup nativehost build; the zenoh router (zenohd)
nros setup native --rmw xrcehost build; the Micro-XRCE-DDS agent
nros setup native --rmw cycloneddshost build; Cyclone DDS runtime + idlc
nros setup qemu-arm-freertosarm-none-eabi-gcc, patched qemu-system-arm, FreeRTOS + lwIP sources, zenohd
nros setup qemu-arm-nuttxarm-none-eabi-gcc, qemu, NuttX sources
nros setup qemu-riscv64-threadxriscv64-*-gcc, qemu, ThreadX/NetX sources
nros setup threadx-linuxThreadX POSIX-sim sources
nros setup qemu-esp32-baremetalthe ESP32-C3 QEMU + bare-metal (esp-hal) toolchain bits
nros setup zephyrthe Zephyr west workspace + Zephyr SDK bits
nros setup mps2-an385 / stm32f4 / qemu-arm-baremetalbare-metal arm-none-eabi-gcc + qemu

Useful flags:

nros setup --list            # every package in the index + its version
nros setup <board> --dry-run # resolve + print the plan, fetch nothing
nros setup --licenses        # license-gated packages + how to install them
nros setup --tool qemu       # one tool by name
nros setup --source freertos-kernel   # one source package by name

Each board’s exact package set lives in the index; run nros setup <board> --dry-run to see precisely what a board pulls. See Supported Boards for the full board list and nros CLI for every subcommand.

Heads-up before your first example. Every nano-ros example (Linux talker, FreeRTOS talker, …) connects to its RMW host daemon at startup — zenohd for zenoh, the Micro-XRCE-DDS agent for xrce. Cyclone DDS is in-process — no separate daemon — so the heads-up below doesn’t apply if you ran nros setup … --rmw cyclonedds. nros setup … --rmw <rmw> installs the daemon into the nros store (~/.nros/sdk/<tool>/<version>/bin/; the cache root is ~/.nros/sdk/ — toolchains, transports, and daemons all land under there); you must then run it in a dedicated terminal before launching any example. For zenoh, put the store binary on PATH once and run it:

export PATH="$(dirname "$(ls -d ~/.nros/sdk/zenohd/*/bin/zenohd | tail -1)")":$PATH
zenohd        # leave running for the whole session

Without it the talker blocks forever on Executor::open with no output. Default ports: tcp/127.0.0.1:7447 on POSIX, tcp/10.0.2.2:7451 on QEMU FreeRTOS (Slirp forwards to host). Mismatch = silent hang; see Troubleshooting — First 10 Minutes.

After provisioning, follow the per-platform starter page (FreeRTOS, Zephyr, NuttX, …) for the board’s build + run steps.

Rust-only consumers

nano-ros is source-only — nothing is published to crates.io (decision 2026-05-14). The full nros crate can’t be published because it depends transitively on C/C++ submodules (zenoh-pico, mbedtls); nros-core isn’t carved out for crates.io either, to avoid a hybrid distribution model with version drift between the crates.io snapshot and in-repo HEAD.

Rust packages consume nano-ros one of two ways:

  • Registry-style names + patch redirection (what every shipped example uses): declare nros = { version = "*" } (+ msg crates) and let nros sync write the # nros-managed [patch.crates-io] block into .cargo/config.toml, resolving the names into the checkout (NROS_REPO_DIR names it). Copy the project anywhere; re-run nros sync to re-point.
  • Explicit path dependency on the in-workspace checkout:
[dependencies]
nros = { path = "src/nano-ros/packages/core/nros",
         default-features = false,
         features = ["std", "rmw-cffi", "platform-posix", "ros-humble"] }

Each Rust package carries its own .cargo/config.toml patch entries when needed — see examples/templates/multi-package-workspace/src/pkg_rust_publisher/ for the pattern.

Pinning a version

Consumers pin the source tree, not a package: clone (or submodule) at a nros-v<X.Y.Z> git tag —

git clone --branch nros-v0.5.0 https://github.com/NEWSLabNTU/nano-ros.git
# or, as a submodule in your project:
git submodule add -b main https://github.com/NEWSLabNTU/nano-ros third_party/nano-ros
git -C third_party/nano-ros checkout nros-v0.5.0

The tag names the bundle version: the nros CLI built from that tree prints the same number (nros versionnros 0.5.0; the scripts/check-version-lockstep.sh gate + bootstrap.sh shell-doctor enforce binary↔tree agreement), and the tree’s committed nros-sdk-index.toml pins the exact toolchain/SDK versions nros setup will provision for it. One tag therefore fixes the whole surface: runtime source, CLI behavior, and provisioned toolchains. Upgrading = checking out a newer tag and re-running ./scripts/bootstrap.sh (rebuilds the CLI to match) + nros setup (reconciles the SDK store against the new index; already-current packages are no-ops). See versioning for how the bundle number is chosen.

Contributor setup (working on nano-ros itself)

Contributors clone the repo and drive everything through just. The just setup recipes are thin wrappers over the same nros setup index — just <module> setup calls nros setup <board> under the hood, so the toolchains a contributor gets are identical to a user’s.

git clone https://github.com/NEWSLabNTU/nano-ros.git
cd nano-ros
./scripts/bootstrap.sh base   # path A from §1 above; gets rustup + just + nros
source ./activate.sh          # OR: direnv allow / source ./activate.fish
just setup all                # provision every supported board's SDK/toolchain

Diagnose missing tools (read-only):

just doctor tier=all

Provision one module:

just freertos setup           # → nros setup qemu-arm-freertos
just nuttx setup              # → nros setup qemu-arm-nuttx
just threadx_linux setup      # → nros setup threadx-linux

Docker environment

For a containerized environment with QEMU 7.2+:

just docker build
just docker shell      # Interactive shell with all tools
just docker test-qemu  # Run QEMU tests in container

Migrating from a pre-140 checkout

If you were on a nano-ros version that still had just install-local, see migration-install-local-removal.md for the one-page rewrite.

Next Steps