FleetlessFleetlessdocs
Start/Getting Started

Getting Started

The afternoon path: install the bridge → connect a robot → expose a datapoint → read it from a first app. No robot required — a two-line fake publisher gets you real data flowing.

0. Prerequisites

  • Ubuntu 22.04 with ROS 2 Humble for the bridge — on a robot, or in a ros:humble container if you just want to try it.
  • curl, gnupg and ca-certificates before step 1. The ros:humble image ships none of them, and without them the very first command fails.
  • Node 22 (nvm use 22) for the last step.

Fleetless is in closed beta. New organizations are created in small rounds: leave your address on the waiting list and we write when a round opens. If you already have an account, sign in.

1. Sign in and create an app

Open https://console.fleetless.dev and sign in. This is the developer identity — not an end user of your app. Create your first app from the Apps page.

2. Create a robot and get its token

In the console’s Robots page, create a robot. This mints a Fleetless token (frt_...) that binds one bridge process to exactly this robot. Keep it; you’ll pass it as FLEETLESS_TOKEN.

3. Install and run the bridge

Add the repository first. apt has no idea the repository exists until you tell it:

sh
sudo apt-get install -y curl gnupg ca-certificates
curl -fsSL https://apt.fleetless.dev/key.gpg | sudo tee /usr/share/keyrings/fleetless.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/fleetless.gpg] https://apt.fleetless.dev humble main" \
  | sudo tee /etc/apt/sources.list.d/fleetless.list
sudo apt-get update
sudo apt-get install -y ros-humble-fleetless-bridge

The package is Architecture: all and serves amd64 and arm64 from one build.

Upgrading an already-connected fleet? Upgrade every robot's package before, or in the same window as, the cloud that requires the new protocol version — never after. The bridge and the cloud speak a versioned wire protocol (PROTOCOL_VERSION), and the cloud's version check is exact, not a minimum: a bridge announcing any other version is refused outright — no telemetry, no cameras, no jobs — until its package is updated. There is no grace period and no dual-format acceptance, by design: a cloud and a bridge that disagree about the wire should not pretend otherwise. So a cloud deploy that bumps the protocol version takes every robot still running the old bridge package off the fleet the moment it lands — but it does not go quiet about it. bridge.launch.py respawns the process unconditionally on exit, so a refused robot does not sit offline: it reconnects, is refused again, exits and respawns on a five- second delay, indefinitely, writing a fresh protocol_mismatch to last_hello_error on every pass. An operator watching such a robot sees a steady five-second reconnect loop in its logs, not silence — that loop is the expected appearance of a version refusal, not a different fault. It stops only once that robot runs sudo apt-get update && sudo apt-get install --only-upgrade ros-humble-fleetless-bridge. Sequence a fleet-wide protocol bump as: upgrade the bridge package on every robot first, then deploy the cloud — or accept the outage window and upgrade robots as fast as reachability allows. Either way, plan the order; do not discover it live. The console's robot detail page and last_hello_error on GET /api/robots/:id name the mismatch (protocol_mismatch, with both versions) when a robot is refused for this reason.

Then run it:

sh
source /opt/ros/humble/setup.bash
source ~/your_ws/install/local_setup.bash   # see the warning below
export FLEETLESS_TOKEN=frt_...
ros2 launch fleetless_bridge bridge.launch.py

FLEETLESS_CLOUD_URL defaults to wss://api.fleetless.dev/bridge, so against the hosted platform there is nothing else to set. Run it without a token and it stops with a sentence that tells you what to do rather than a stack trace:

FLEETLESS_TOKEN is not set. Create the robot in the Fleetless console and
pass its token via the launch file or environment.

Source your own workspace overlay, not just /opt/ros/humble. This is the one that costs an evening. A bridge started without the robot's overlay starts, connects, and reports itself healthy — and then cannot resolve your own message types (unknown type 'your_msgs/msg/Thing': No module named 'your_msgs'), and cannot find the package:// meshes your URDF references, so the console tells you they are "not found in the robot's workspace" while they sit right there.

The robot should appear connected in the console within a few seconds.

4. No robot? Publish fake data instead

Something you can copy, paste and run with no robot, no bridge development setup, nothing but a sourced ROS2 environment sharing the bridge’s ROS_DOMAIN_ID.

The one-liner, if you just want a value to exist:

sh
ros2 topic pub /battery std_msgs/msg/Float32 "data: 87.5" -r 1

The twenty-line version, if you want a value that actually changes — save as fake_battery.py and run with python3 fake_battery.py:

python
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import Float32


class FakeBattery(Node):
    def __init__(self):
        super().__init__('fake_battery')
        self.publisher = self.create_publisher(Float32, 'battery', 10)
        self.level = 100.0
        self.create_timer(1.0, self.tick)

    def tick(self):
        self.level = max(0.0, self.level - 0.1)
        msg = Float32()
        msg.data = self.level
        self.publisher.publish(msg)


def main():
    rclpy.init()
    rclpy.spin(FakeBattery())


if __name__ == '__main__':
    main()

Run it in the same network namespace as the bridge, with the same ROS_DOMAIN_ID. Either publisher gives the bridge’s introspection something real to show in the next step.

5. Expose the value as a datapoint

In the console, open your robot’s Introspection tab — a browsable view of its live ROS graph. Find /battery, use “→ adopt” as a shortcut (or configure it by hand in Configuration): give it a slug (e.g. battery_percentage), a unit (%) and a range (0100). Configuration changes are draft → publish — publish once you’re happy.

Once published, the robot’s generated API has a new datapoint at that slug.

6. Read it from a first app

sh
npm i @fleetless/sdk
ts
import { createClient } from '@fleetless/sdk'

const client = createClient({
  apiUrl: 'https://api.fleetless.dev',
  appIdentifier: 'warehouse_dash', // the app identifier shown in the console
})

await client.auth.login('you@example.com', 'your-password')

// or the hosted login redirect — Fleetless serves the sign-in page:
// await client.auth.beginHostedLogin({ clientId, redirectUri })

const battery = await client.datapoints.get('robot-uuid', 'battery_percentage')
console.log(battery.value, battery.timestamp_ms)

client.datapoints.subscribe('robot-uuid', 'battery_percentage', {
  onEvent(event) { console.log('live:', event.value, event.timestamp_ms) },
})

That’s the whole afternoon path. From here:

  • Concepts explains the model behind what you just did — services, slugs, roles, disconnect handling.
  • SDK Reference covers actions, services, publishers, cameras and URDF rendering — everything datapoints don’t.
  • API Reference if you’re calling the REST/realtime surface directly instead of through the SDK.