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:humblecontainer if you just want to try it. curl,gnupgandca-certificatesbefore step 1. Theros:humbleimage 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:
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:
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:
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:
#!/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 (0–100). 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
npm i @fleetless/sdk
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.