Nodofi Docs

Nodofi Robot Agent

Run Robot projects from Studio on a real Raspberry Pi, Jetson, or PC with a USB Arduino or ESP32. The agent joins the relay so Studio reaches the robot from anywhere — no public IP required.

Overview

The Robot Agent is a small Node.js process that runs on the robot. Studio talks to it over the relay to start, stop and deploy programs. The program itself (the flow's blocks and the engine that runs them) is sent with each run, so the robot always behaves exactly as it did in the simulator.

ModeHow it works
Tethered runStudio streams live sensor values back to the canvas while the program runs. The robot stops automatically if the Studio tab is closed or the connection drops (heartbeat timeout, default 3 s).
DeployThe program is saved on the robot and starts on its own — no Studio needed. Survives reboots when the agent runs as a systemd service.
STOPEnds any run at once and sets every motor and output to off, from Studio or from the keyboard.

Safety: the program runs in a child process; the agent process owns the hardware. Whenever a run ends — for any reason, including a crash or a lost heartbeat — every motor stops and every output goes off.

Before you start

  • Node.js 18 or later on the robot. Check with node -v.
  • On Raspberry Pi, use 64-bit Raspberry Pi OS. The Pi Zero W and Pi 1 use ARMv6, which has no official Node.js 18 build — use the Zero 2 W or Pi 3+ instead, or build Node.js from source.
  • The relay URL — the same one Studio uses (RELAY_URL in Studio's .env). Both sides must connect to the same relay.
  • The robot-agent folder on the robot. Copy it over SSH or clone the repo.
  • Optional: an Arduino, ESP32, Pico or Teensy on USB for motors and analog sensors. Flash it once with StandardFirmata (Arduino IDE → File → Examples → Firmata → StandardFirmata).

Install & configure

1. Copy the folder to the robot

scp -r robot-agent pi@raspberrypi.local:~/robot-agent

Or, if the full repo is already on the Pi, just cd robot-agent.

2. Install dependencies

cd ~/robot-agent
npm install

This installs required packages and tries the optional hardware libraries. Failed optionals are logged but are not errors — install only what your robot needs (see Hardware libraries).

3. Create the .env file

cp .env.example .env
nano .env

Set at minimum:

# The relay Studio uses — must be the same relay
RELAY_URL=https://relay.nodofi.com

# Unique name for this robot on the relay
ROBOT_NAME=my-robot-1

Pick any name for ROBOT_NAME. It must be unique on the relay — Studio finds the robot by this name.

First run & pairing key

Start the agent for the first time to generate a pairing key:

npm start

The output looks like:

Nodofi Robot Agent v1.0.0
Robot name : my-robot-1
Relay      : https://relay.nodofi.com
Pairing key: abc123XYZdef456    ← copy this now
Connected.
Warning: Copy the pairing key now. It is stored in data/agent.json and printed once for convenience. If you lose it, delete data/agent.json and restart — a new key is generated and you re-pair in Studio.

Pair with Studio

  1. Open a Robot project in Studio (create one with New project → Robot).
  2. Click the Run on dropdown in the toolbar → Add a robot…
  3. Fill in: Relay URL (same as RELAY_URL), Robot name (same as ROBOT_NAME), and the Pairing key printed on first start.
  4. Select the robot in the Run on dropdown, then press Start. Studio sends the program and streams live sensor values back to the canvas.
Note: If the Hardware panel shows any parts as simulator-only on this robot, those parts need an additional library. See Hardware libraries for the install command.

Hardware libraries

All hardware libraries are optional — install only what your robot uses. Set HARDWARE=mock in .env to run any program without touching a pin.

LibraryBoardsWhat it enables
pigpio Raspberry Pi 1–4, Zero 2 W Full GPIO: digital I/O, hardware PWM, servo pulses, microsecond timing for ultrasonic distance and wheel encoders. Run the agent with sudo.
sudo apt install pigpio
npm install pigpio
sudo npm start
node-libgpiod Raspberry Pi 5, Jetson, BeagleBone, other Linux boards Digital in/out only (no hardware PWM). Put motors, servos and analog parts on a USB board.
sudo apt install libgpiod-dev
npm install node-libgpiod
i2c-bus Any Linux board with I²C I²C chips: MPU-6050, BNO055, QMC5883L, BME280, VL53L0X, TCS34725, LCD, OLED, 7-segment. Enable I²C in raspi-config first.
npm install i2c-bus
firmata + serialport Arduino, ESP32, Pico, Teensy over USB Motors, servos, PWM and analog inputs on the USB board. Flash StandardFirmata once.
npm install firmata serialport
rpi-ws281x-native Raspberry Pi (GPIO18 / 12 / 10 / 21) NeoPixel / WS2812B strips. Requires pigpio and sudo.
npm install rpi-ws281x-native
rpicam-vid / ffmpeg Pi Camera (CSI) / USB webcam Camera blocks: Find color, Motion, QR code, Take photo, Light level.
sudo apt install rpicam-apps   # Pi Camera (Bookworm)
sudo apt install ffmpeg         # USB webcam

Start at boot

Create a systemd service so any deployed program starts automatically when the Pi powers on:

[Unit]
Description=Nodofi Robot Agent
After=network-online.target
Wants=network-online.target

[Service]
WorkingDirectory=/home/pi/robot-agent
ExecStart=/usr/bin/node src/agent.js
Restart=always
RestartSec=5
User=root

[Install]
WantedBy=multi-user.target

Save the file to /etc/systemd/system/nodofi-robot.service, then:

sudo systemctl daemon-reload
sudo systemctl enable --now nodofi-robot
sudo systemctl status nodofi-robot

View live logs: journalctl -u nodofi-robot -f
Restart after changing .env: sudo systemctl restart nodofi-robot

Agentic AI drives the robot

Studio's Agentic AI pane can drive a robot from a goal in plain words, such as "have the robot find the red ball". It never sends motor values: it runs your Robot projects as skills, picked by their name and description, on the Simulator or a paired robot. The project opens on the canvas, so you watch the robot move while the agent works.

  1. Build and test each skill in the simulator first. Give it a clear name and a one-line description ("Obstacle avoider — drives forward and turns away from walls"): that is what the agent reads.
  2. In the Agentic AI pane choose Run Flow tools in Studio, set the relay and the planner (a model worker such as JozieAILLM2, planner model gemma4).
  3. Under Robots the agent may move, tick the Simulator (on by default) and any paired robot.

Guardrails

GuardrailWhat it does
Allowed robots onlyReal robots are off until ticked. A goal naming a robot that isn't allowed stops the run; the agent never moves a different robot instead.
Your approval"Ask me before a real robot moves" (on by default) shows the skill, its inputs, the robot and the time limit. Declining ends the whole run.
Time limit per skillDefault 20 s, at most 120 s; the model can ask for less, never more.
Skills onlyNo motor values or code from the model; at most 3 skills per goal, one per step; an immediate repeat answers instead of moving again.
StopThe pane's Stop or the canvas STOP ends a skill at once; the robot's heartbeat watchdog still applies if the tab closes.

Skill inputs (Agent input)

An Agent input block (Events & memory) gives a skill a parameter, so one "Forward" skill handles "move forward 37 cm". Its settings: a name (cm), number or text, a description the agent reads ("distance to drive, in cm"), a default, a minimum and maximum, and The agent must set it (on by default).

  • Min and max are enforced inside the program, on the robot, so no planner reply can exceed them; a larger value is clamped and the agent is told.
  • No guessed defaults: a required input the agent leaves out (or gives as something that isn't a number) stops that step, and the agent is told what is missing. Start without an agent uses the default.
  • Units: the planner converts the goal's units ("half a meter" → cm = 50; "2 meters" with a 100 cm maximum → two runs of 100). A value written with the input's own unit ("30 cm") is accepted; any other unit is never quietly converted.

Example: Forward N cm

When started → Wheel odometer → Set variable from, and When started → Drive 40 / 40. Every 50 ms → Wheel odometer, Get variable from and Agent input cm → Formula x - y >= z → If → Stop motors. Add a Display of the distance travelled and the agent reports it. Make Turn left and Turn right the same way with a degrees input and the Compass. On a real robot, distance needs wheel encoders and turns a compass or IMU.

Missions on the robot

With Mission on a robot (Agentic AI pane), the Agentic AI runs on the robot itself. Studio sends the mission in plain words, the skills it may use (compiled Robot projects, nothing else can run) and limits; the robot plans each step with a language model, runs one skill, reads what happened, and repeats until the mission is done. Studio can be closed meanwhile; reopening shows the log.

PlannerHow
On boardThe robot's own Ollama (OLLAMA_HOST) — works with no network once the mission is sent. Install Ollama on the robot and ollama pull gemma4; Studio lists the models it finds.
CloudAn AI Worker on the relay (e.g. joined as JozieAILLM2 on a GPU machine).

Guardrails (nobody approves each move)

  • Only the skills sent with the mission run; never motor values or code.
  • Before anything moves, the model is asked whether the skills can do the mission at all; a clear "no" ends it with the missing ability named (e.g. "fly").
  • A mission time limit (default 10 min, at most 60), a time limit per skill (default 20 s, at most 120) and a maximum number of skill runs (default 20) — set this to what the mission should need.
  • Three planner failures in a row end it; STOP ends it at once; every way it ends switches the outputs off; a run or deploy from Studio replaces it; a mission never resumes after the agent restarts.
  • Sending one needs your confirmation, which spells out that the robot will move on its own.

Planning uses Ollama's JSON mode with thinking turned off, which is reliable and several times faster with thinking models such as gemma4.

Persona link

The agent can also join Persona's Drone Operations companion as a ground vehicle. In Persona: Fleet → + Register Drone, connection mode Remote, vehicle class Ground; copy the droneUuid and uploadToken from its card into PERSONA_VEHICLE_UUID and PERSONA_UPLOAD_TOKEN, and set PERSONA_RELAY_URL to the relay Persona uses.

Robot → PersonaPersona → robot
Telemetry (heading, front distance, speed; a fixed position if set)Pause / abort / emergency stop: stop everything
Photos, soil readingsManual Control: drive with the sticks, capped speed, stops if input pauses for 1 s
Resume, return home, waypoint missions: refused with the reason (no GPS navigation)

Every command from Persona must be signed with the vehicle's token and be under a minute old; anything else is ignored, so a stranger on the relay can't drive or stop the robot. This needs a Persona server that signs its commands. Load the hardware once (run or deploy a program from Studio) before using Manual Control.

Parts reference

Every part type from Studio's Hardware tab and whether it is driven on real hardware or needs a library. Studio's Hardware panel shows the same list when the agent is connected.

PartPins / busNotes
DC motor (3-pin driver)EN (PWM), IN1, IN2EN needs hardware PWM; use a USB board if available.
DC motor (2-pin driver)IN1 (PWM), IN2 (PWM)Both pins need PWM.
Continuous-rotation servoSignal (servo PWM)Requires pigpio or a USB board.
Servo motor (SG90 / MG996R…)Signal (servo PWM)Same. Power from its own 5–6 V supply.
Stepper — ULN2003 (28BYJ-48)IN1–IN4 (digital)Half-step; works on any GPIO.
Stepper — A4988 / DRV8825STEP, DIR, ENABLESet current limit first.
ESC (brushless motor)Signal (servo PWM)Sends arming pulse on start. Remove propellers while testing.
LED, RGB LEDDigital / 3 × PWMAny GPIO; RGB needs PWM.
NeoPixel strip (WS2812B)GPIO18 / 12 / 10 / 21Needs rpi-ws281x-native and sudo.
Buzzer, relay, laser, vibrationDigital or PWM outAny GPIO.
Ultrasonic HC-SR04 / HC-SR04PTRIG, ECHOMust be on Pi GPIO with pigpio — Firmata cannot time the echo pulse.
Sharp IR distanceAnalog outNeeds an analog pin on a USB board (Pi has none).
VL53L0X laser / ToFI²CNeeds i2c-bus.
Line sensor (3- or 5-channel)Digital in (×3 or ×5)Any GPIO.
Color sensor (TCS34725)I²CNeeds i2c-bus.
IMU (MPU-6050)I²CNeeds i2c-bus. Integrates gyro Z-axis for heading.
BNO055 / QMC5883L / HMC5883LI²CNeeds i2c-bus. Fused absolute heading.
Wheel encoderChannel A (interrupt)Must be on Pi GPIO with pigpio for edge counting.
DHT11 / DHT22Digital (pigpio timing)Must be on Pi GPIO with pigpio.
BME280I²CNeeds i2c-bus. Temperature, humidity and pressure.
Buttons, bumpers, PIR, hall…Digital inAny GPIO.
Potentiometer, joystick, analog sensorsAnalog inNeeds analog pin on a USB board.
LCD 16×2, OLED 128×64, 7-segmentI²C or 2-wireNeeds i2c-bus.
Raspberry Pi Camera (CSI)Ribbon cableDriven by rpicam-vid (Bookworm) or libcamera-vid (Bullseye).
USB webcamUSBDriven by ffmpeg.

Configuration (.env)

SettingDefaultDescription
RELAY_URLrequiredThe relay Studio uses. Must match exactly.
ROBOT_NAMErequiredThis robot's unique name on the relay.
DATA_DIR./dataWhere the pairing key and deployed program are stored.
HARDWAREautoauto: drive real pins. mock: touch nothing (for testing flows on any computer).
SERIAL_PORTauto-detectThe USB board's port if auto-detection picks the wrong device (e.g. /dev/ttyACM0).
PAIRING_KEYfrom agent.jsonA fixed pairing key instead of the auto-generated one.
HEARTBEAT_TIMEOUT_MS3000How long (ms) a tethered run continues without a Studio heartbeat before the robot stops.
OLLAMA_HOSThttp://127.0.0.1:11434The on-board model for missions planned on board.
PLANNER_TIMEOUT_MS120000How long one planning step may take before it counts as a failure.
PERSONA_VEHICLE_UUID, PERSONA_UPLOAD_TOKENoffLink this robot to Persona (see Persona link).
PERSONA_RELAY_URLRELAY_URLThe relay Persona uses, if different.
PERSONA_TELEMETRY_MS, PERSONA_MAX_MANUAL_SPEED2000, 60Telemetry interval; speed cap for Persona's Manual Control.
PERSONA_LATITUDE, PERSONA_LONGITUDEnoneA fixed position for Persona's map (no GPS on Studio robots).

Troubleshooting

Studio can't find the robot

Confirm the agent is running (npm start or systemctl status nodofi-robot), that RELAY_URL exactly matches Studio's relay (same protocol, host and port), and that ROBOT_NAME matches what you entered in Studio's Add a robot… dialog. The agent logs Connected. when it has successfully joined the relay.

Parts show as "simulator-only"

Install the library the part needs (see Hardware libraries). For pigpio, run the agent with sudo. For I²C, enable I²C in raspi-config and reboot. For the USB board, confirm it is flashed with StandardFirmata and connected; the agent logs which serial ports it tried.

Ultrasonic sensor or encoder always reads null

These require microsecond-level pulse timing. Move those pins to the Raspberry Pi's own GPIO (pi:GPIOxx in the Hardware tab) and run the agent with sudo (pigpio).

Motors don't move

Motor drivers take power from their own battery, not the Pi's pins — check the battery. Set HARDWARE=mock to verify the flow reaches the motor block; if it does, the fault is in the wiring. EN (PWM) on a Pi must be on a hardware-PWM pin (GPIO12, 13, 18 or 19) or on a USB board.

Agent crashes (pigpio error)

pigpio must run as root. Use sudo npm start or set User=root in the systemd unit. Only one process can open the pigpio device at a time.

Camera: "no picture"

For a Pi Camera, enable it in raspi-config and reboot. Test with rpicam-still -o test.jpg. For a USB webcam, install ffmpeg and test with ffmpeg -f v4l2 -i /dev/video0 -frames 1 test.jpg.

Safety

Warning: Real robots move.
  • Keep the robot on a stand (wheels off the ground) the first time you run a new program.
  • Keep STOP within reach — it cuts motors immediately.
  • Power motors from their own battery, never from the Pi's GPIO pins. Motor current on a GPIO pin damages the board.
  • Never leave a deployed program unattended until you trust it handles all edge cases.
  • 3.3 V logic — the Pi's GPIO is 3.3 V. A 5 V signal on a Pi pin can destroy it. Use 3.3 V sensors, a voltage divider, or a level shifter.
  • Propellers: remove them from brushless motors during all testing.
  • Relays and mains voltage are dangerous. Keep projects to low-voltage loads.