Controls guide
This page drives a LEGO Technic Move Hub from a browser tab. Everything below is a control you can see on screen: what it does, when you would use it, and where it can bite.
A few words come up often, so they are explained once here:
- Hub — the grey brick with the motors inside it. The page talks to it directly over Bluetooth.
- Bluetooth — the short-range radio link between your phone or computer and the hub. No pairing in system settings is needed; the browser handles it.
- Drive motor A and B — the two motors that turn the wheels.
- Steer motor — the separate motor that turns the front wheels left and right.
- Motion sensors — the hub contains sensors that measure tilt and sudden movement. The page calls these telemetry.
Always on screen
These sit outside the tabs, so they are reachable no matter which section you are looking at.
Connect hub
What it doesOpens the browser's Bluetooth device chooser so you can pick your hub. Once a hub is attached, this button disappears and the status chip, battery, pad button, tab bar, STOP ALL and the dashboard all appear in its place.
When you'd use itOnce, at the start of every session.
Watch outConnecting calibrates the steering: the hub sweeps the front wheels to both ends of their travel. Lift the model or leave the wheels free before you press it.
It works in this order: the browser shows you its own list of nearby Bluetooth devices, you pick the hub, and the page then reads what the hub says about itself — how many motors, which lamps, which sensors — before the dashboard appears. Nothing is remembered between sessions, so you pick it each time.
Connect a hub (large button on the landing page)
What it doesThe same thing as Connect hub, offered in the middle of the introduction page so you do not have to hunt for the small one. The whole introduction page disappears once a hub is attached.
Both buttons run the same connect.
Status chip (coloured dot plus a short line of text)
What it doesShows the state of the link to the hub. Tapping it jumps straight to the Hub tab, where the full status text lives.
When you'd use itWhenever you want to know why something is not responding.
The dot is green when the link is healthy, amber for a warning — a detected collision, for example — and red when the hub has gone. When everything is fine the dot stands alone; words appear beside it only when something is wrong, which keeps the bar clear while you drive.
Battery reading
What it doesShows the hub's remaining charge as a percentage. A small q followed by a number sometimes appears next to it — that is the number of commands waiting to be sent, refreshed four times a second. It is hidden whenever the number is zero.
Watch outA q number that keeps climbing means commands are backing up faster than the hub can take them. That is usually the first sign of a link about to fail.
pad: OFF / pad: ARMED
What it doesTurns the gamepad control loop on and off. When it reads ARMED, your game controller is driving the car. The short text beside it is the first part of the controller's name, or "not connected" if the browser cannot see a pad.
When you'd use itEvery time you want to drive with a controller, and again to stop.
Watch outArming the pad means the car can move without you touching the screen. The loop disarms itself if the link drops, if you press STOP ALL, or on any error inside the loop.
STOP ALL
What it doesThe panic button. It drops the steering out of angle-holding mode, stops every motor, and disarms the gamepad loop. It stays in the same place on every tab and is never hidden behind navigation.
When you'd use itThe moment the model is heading somewhere you did not intend.
Watch outSimply stopping the motors is not enough while a pad is armed — the loop would re-command them on the next frame — which is why this control also switches the pad off. You have to press pad: OFF's counterpart again to resume driving.
The order matters and is deliberate: the things that issue commands are silenced first — the gamepad loop, the steering controller, the combined drive frame — and only then is power cut to the motors. Stopping the motors while something is still commanding them would last only until its next frame.
Tab bar: Drive, Setup, Lights, Hub, Motion, Macros, Debug
What it doesSwitches between the seven sections. The current one is underlined. On a keyboard, the number keys 1 to 7 do the same thing, in that order.
Watch outThe number keys are ignored while you are typing in a text box, so the Debug tab's fields still work normally.
Drive tab
Everything you touch while the car is moving.
Steering readout
Not a control. It reads pos 0 → 0: the steer motor's current position, then the position it is being asked to hold. If it also says (set zero!) the steering has no reference point yet — see Set zero in the Setup tab.
mode (Raw / Steer)
What it doesChooses how the steering motor is commanded.
- Raw — stick drives motor power (the default). Your input becomes motor power directly. Push harder, the motor turns faster. Let go, it stops.
- Steer — hold an angle (needs a zero). Your input becomes a target angle, and the motor works to hold that angle against the springs and the road. If you have not set a zero point yet, switching to this mode captures one from wherever the wheels are standing.
When you'd use itRaw is the safe default and always works. Steer gives you proportional, self-centring steering once the wheels are calibrated.
Watch outThis dropdown reports what is actually running rather than what you last chose. If the steering runs away or loses its position feedback, it drops itself back to Raw and the dropdown will change on its own. Switching from Steer back to Raw also stops the steer motor, so it is never left pushing.
Center
What it doesCommands the steering back to its zero position.
When you'd use itTo straighten the wheels before you set off.
◀ LEFT and RIGHT ▶
What it doesPress and hold to steer. In Raw mode the button spins the steer motor for as long as you hold it. In Steer mode it commands full lock in that direction, and releasing hands control back — which, with auto-return on, means the wheels come back to centre.
Watch outReleasing is detected in every way the finger can leave the button, including sliding off the edge, so a motor cannot be left running by a missed release.
Drive A and Drive B rows
Each drive motor gets its own row, built from whatever the hub reports it has. The row heading shows the motor's name, its port number, and its live speed.
- power — a slider from 10 to 100, starting at 50. It sets how hard the FWD and REV buttons below it push. It does not move anything on its own.
- FWD ▲ / REV ▼ — press and hold to run that motor forward or backward at the power set above. Releasing sends a stop.
When you'd use itTesting a build on the bench, or nudging the model without a gamepad.
Watch outWhen the hub has fused the two drive motors into a single unit — which it does in the default drive behaviour — both rows are routed to that combined unit, because addressing an individual motor at the same time confuses the hub. Expect both rows to move both motors in that situation.
Measured
What it doesSwitches on a live attitude readout that sits with the drive controls: roll — · pitch — · yaw — becomes three angles from the hub's orientation sensor, with the steer motor's position on a second line. Off by default, and remembered between sessions.
When you'd use itWhen you want the numbers while you are driving, without leaving the Drive tab for the Motion tab.
Watch outSwitching it on subscribes to two streams — the orientation sensor and the steer motor's position — and the hub is sensitive to how many subscriptions are running. The warning under telemetry in the Hub tab applies here too.
Watch outA port can only report one thing at a time. If the Debug tab's probe or the Motion tab already holds a stream, the note under the readout says stream unavailable: and names which one failed; the rest still works.
The steer line reads — (set zero in Setup) until the steering has a reference point. The angles here are the hub's own, uncorrected: unlike the Motion tab there is no Centre here, so how the brick is mounted in the model shows up in the numbers.
Drive behaviour → mode
What it doesChooses how your gamepad's inputs are turned into motor commands.
| Option | What it does |
|---|---|
| Typical module behaviour — the hub drives itself (default) | Sends speed, steering and lights as one combined instruction and lets the hub work out the rest. This is how the LEGO app drives the model. |
| Linked — both motors together | Both drive motors run as one, and the steering is commanded separately by this page. |
| Independent — A on triggers, B on stick | The two drive motors are controlled separately: motor A from the triggers, motor B from the right stick. |
| Tracked — one stick, tank steering | One stick drives both motors; pushing it sideways counter-rotates them so the model turns on the spot, the way a tracked vehicle does. The steer motor is left free on the other stick for whatever your build uses it for. |
Watch outChoosing the first option arms the combined instruction, which sweeps the steering rack and takes a few seconds. The readout below tells you it is happening. If the hub refuses, the page falls back to Linked by itself and says why.
Watch outTracked mode needs one of the two drive motors set to REVERSED in Setup, because they are mounted facing opposite ways. Without it, turning and throttle come out swapped, and the page will tell you so in the readout.
Drive behaviour → on release
What it doesDecides what a motor does when a control returns to zero.
| Option | What it does |
|---|---|
| Brake — stop dead (default) | The motor resists turning and the model stops where it is. |
| Coast — free-wheel | Power is simply removed and the model rolls to a stop. |
When you'd use itCoast feels more natural on a car; brake is better for precise positioning and on a slope.
Watch outChanging this applies to every motor at once, and it re-sends a stop immediately so you feel the new behaviour straight away rather than on the next release.
Live readout
Not a control. Reads A 0 · steer 0 · trim 0 and shows what the gamepad is currently commanding, plus a row of filled and empty circles for the six lamps. In Independent mode a second throttle figure appears. This line is also where mode-change messages and failures are printed.
Setup tab
Calibration and set-and-forget tuning. Nothing here needs to be touched while driving.
Steering calibration → Set zero
What it doesRecords the steering motor's current position as straight ahead. Everything the steering does afterwards is measured from that point.
When you'd use itAfter you have jogged the wheels to look straight, and any time the steering has slipped.
Watch outThis changes calibration, so on a gamepad it is deliberately bound to a button you must hold for a second rather than tap.
turn speed
What it doesHow fast the steer motor turns. Slider from 10 to 100, starting at 40.
When you'd use itLower it if the steering snaps around too abruptly for your build; raise it if it feels sluggish.
Advanced steering (fold-out)
What it doesHides four settings and two checkboxes that most people never need. Tap the heading to open it.
max angle
What it doesHow far from centre, in degrees, full lock is. Slider from 5 to 720 in steps of 5, starting at 90°.
When you'd use itMatch it to your model's actual steering travel. Too large and full stick will drive the rack into its end stop.
kp
What it doesHow hard the steering pushes to correct an error between where the wheels are and where they should be. The slider runs 1 to 50 and is shown as a decimal from 0.1 to 5.0; it starts at 0.3.
When you'd use itOnly in Steer mode. Raise it if the steering is lazy about reaching the angle you asked for; lower it if the wheels twitch or oscillate around the target.
Watch outSetting it high makes the steering unstable rather than sharp.
deadband
What it doesHow close to the target angle counts as "close enough", in degrees. Slider from 0 to 30, starting at 8°.
When you'd use itWiden it if the steering never settles and keeps hunting back and forth.
auto-return to zero on release
What it doesWhen ticked, letting go of the steering brings the wheels back to straight ahead on their own. On by default.
invert steering direction
What it doesSwaps left and right for the steering motor. Off by default.
When you'd use itWhen pushing left turns the wheels right, because of how the steering motor is geared into your build.
Motor direction
What it doesOne button per motor — Drive A, Drive B and Steer — each reading NORMAL or REVERSED. Tapping one flips which way that motor counts as forward, and stops the motor as it does so. Your choice is remembered on this device and comes back next time you open the page.
When you'd use itWhen a motor runs backwards because of how it is mounted, and for Tracked drive mode, which needs one drive motor reversed.
Steer jog — mechanical alignment
What it doesTwo press-and-hold buttons, jog ◀ and jog ▶, that turn the steer motor at a fixed gentle speed regardless of any other setting.
When you'd use itTo physically line up the front wheels straight before pressing Set zero. These duplicate the Drive tab's LEFT and RIGHT buttons and exist here only for that alignment job.
Gamepad feel → axes readout
Not a control. Shows the raw numbers coming off each of your controller's sticks, plus the name of the layout the browser thinks it has. Useful when a pad numbers its sticks differently from the standard and a binding lands on the wrong one.
deadzone
What it doesHow far a stick has to move before the car reacts at all. The slider runs 0 to 50 and is shown as a decimal from 0.00 to 0.50; it starts at 0.15.
When you'd use itRaise it if a worn stick makes the car creep when you are not touching it. Lower it for finer control near centre.
expo
What it doesHow gently the stick responds near the centre. The slider runs 10 to 40 and is shown as 1.0 to 4.0; it starts at 2.0. Higher numbers mean small stick movements do less and the response builds up towards the ends.
When you'd use itRaise it if the car feels twitchy around straight ahead.
max speed
What it doesCaps how fast the car can go, as a percentage. Slider from 20 to 100, starting at 100.
When you'd use itTurn it down to make an indoor run manageable, or when someone new is driving.
steer gain
What it doesCaps how much steering the stick can ask for, as a percentage. Slider from 20 to 100, starting at 100.
When you'd use itTurn it down if full stick is more lock than your model can use.
accel
What it doesChooses how the throttle ramps up when you squeeze a trigger.
- expo (ease in) — the default. Eases in and settles smoothly.
- linear — climbs at a constant rate.
- instant — no ramp at all; the motors get what the trigger says immediately.
When you'd use itInstant is direct but harsh on the gears. Expo is kinder to the drivetrain and to grip.
accel rate
What it doesHow quick that ramp is. Slider from 60 to 600 in steps of 20, starting at 220. Higher is punchier, for both the linear and expo settings.
Button mapping (fold-out)
What it doesLists every action the car can be given and which control on your pad currently triggers it. Tap a row and it changes to "… press a control"; press the button or move the stick you want, and it is bound. Tap the same row again to cancel without changing anything.
When you'd use itWhen a binding does not suit your pad or your habits.
Reset to defaults
What it doesPuts every binding back to the factory layout below.
Default bindings
The names assume an Xbox-style pad. Other controllers use the same positions under different names.
| Action | Default control |
|---|---|
| Steer | Left stick, sideways |
| Throttle A | RT forward, LT backward |
| Throttle B (Independent mode) | Right stick, up and down (up is forward) |
| Tank turn (Tracked mode) | Left stick, sideways |
| Tank throttle (Tracked mode) | Left stick, up and down |
| Steer motor, Tracked mode | Right stick, sideways |
| Brake | A |
| Toggle linked/independent drive | Start |
| Toggle raw/steer mode | B |
| Set steering zero | Back — hold for one second |
| Cycle RGB LED | Y |
| All lights on/off | D-pad Right |
| Lamp 1 | D-pad Up |
| Lamp 2 | D-pad Down |
| Lamp 3 | D-pad Left |
| Lamp 4 | X |
| Lamp 5 | Left stick click |
| Lamp 6 | Right stick click |
| Trim steer left | LB |
| Trim steer right | RB |
Watch outIn Tracked mode the steer motor deliberately sits on the right stick, because the left one is already counter-rotating the tracks. Sharing one stick between both used to drive the steer motor into its end stop and stall it there.
Watch outSet steering zero is the one binding that needs a deliberate one-second hold, because changing calibration mid-drive is destructive.
The trim buttons nudge the steering centre by two units per press, and the trim is only applied while you are actually steering — held at rest it would leave a permanent trickle of power on the motor, which makes it buzz instead of turn.
Lights tab
Nothing here can move the model.
Lamps 1 to 6
What it doesSix buttons, one per lamp on the hub. Tap to turn a lamp on or off. Lit lamps are filled in.
Watch outThe numbers are the hub's own lamp numbering, which is not necessarily the order they appear on your model. Use Chase below to find out which is which.
brightness
What it doesSets how bright the lit lamps are, from 0 to 100. Starts at
- Lamps that are switched off stay off regardless.
Watch outAt 0 the lit lamps are indistinguishable from off.
All on / All off
What it doesTurns all six lamps on or off in one tap, and updates the six buttons to match.
Chase 1→6 (diagnostic)
What it doesLights each lamp on its own for about three quarters of a second, in order, then turns everything off. Each step is written to the Debug log with its number.
When you'd use itTo learn which physical lamp on your model corresponds to which number, so you can bind the right one to a gamepad button.
Watch outIt takes a few seconds and holds the light channel while it runs. Let it finish.
Status LED: Red, Green, Blue, Yellow, Off
What it doesSets the colour of the hub's own small indicator light. Purely cosmetic.
Hub tab
Session bookends: what the link is doing, and what the hub is reporting.
Connection status
Not a control. The full status text for the link, in plain words. This is where the status chip in the top bar takes you.
Discovered ports (fold-out)
What it doesLists the motors, lamps and sensors the hub reported when it connected. The page does not assume a fixed layout; it asks the hub and works with what comes back.
When you'd use itWhen trying an unfamiliar set, to see what the page found.
Each entry reads like steer=0x34 — a name and the number the hub uses for it internally. You never need those numbers, but they are what the Debug tab's probe expects if you ever go there.
refresh battery
What it doesAsks the hub for its charge level right now, rather than waiting for it to report on its own.
telemetry: OFF / ON
What it doesTurns on the stream of motion readings — tilt, acceleration — and the live speed figures for the two drive motors. Off by default.
When you'd use itWhen you want the tilt readout below to show real numbers, or the speed figures on the Drive tab to move.
Watch outThis is the single heaviest load on the Bluetooth link. The motion sensor alone produces around 250 messages a second, and with the motor speed streams on top the hub has been seen to stop responding within a second of connecting. Leave it off unless you want the numbers.
Watch outThe steer motor's speed is not included, because that motor is already streaming its position for the steering to work, and a port can only report one thing at a time.
crash guard: OFF / ON
What it doesWatches the hub's accelerometer and cuts the motors on a sharp impact. Off by default.
When you'd use itDriving somewhere with things to hit.
Watch outIt costs an extra sensor subscription, and the hub is sensitive to how many of those are running. See the telemetry warning above.
tilt readout
Not a control. Reads tilt: — (turn telemetry on) until telemetry is switched on, then shows three numbers for the hub's orientation.
Motion tab
What the hub thinks its own attitude is, drawn rather than tabulated. Nothing here commands a motor.
Attitude
What it doesNot a control. A 3D view of the hub's orientation with the same three angles — roll, pitch and yaw — written underneath it.
Watch outOpening this tab subscribes to the orientation sensor and to the steer motor's position, and drops both again when you leave. That is real load on the Bluetooth link; see the telemetry warning in the Hub tab.
Watch outThe 3D drawing is loaded separately and from the internet. If it cannot load, the note reads 3D unavailable — numbers only and the readouts carry on regardless. The tab is designed to be useful without it.
Centre here
What it doesTakes the hub's current orientation as level and facing away from you, and reports every later reading relative to that. The choice is remembered between sessions.
When you'd use itOnce per model, after the hub is built into it. The hub does not know which way round it was mounted; this is how you tell it.
Watch outIt needs a reading to capture. Pressed before any orientation data has arrived it says no orientation data yet and does nothing.
Watch outYaw is relative and drifts. It is a gyroscope reading with no compass behind it, so it wanders over minutes even while the model stands still. Roll and pitch are held honest by gravity and do not.
Steering
Not a control. A dial and a number showing where the steer motor is standing. It is the motor's shaft angle, not the angle of the road wheels — the gearing between them is whatever your model builds.
Macros tab
A macro is a short piece of JavaScript you write yourself and the page runs for you, so a sequence — drive forward, wait, turn, read a sensor, react — can happen on its own instead of one action at a time under your thumb.
Macro slot
What it doesThe dropdown lists your saved macros; New adds an empty one and Delete removes the one currently selected.
Watch outDelete has no confirmation and no undo. Export first (see below) if you might want it back.
Source
What it doesThe text box where you write the macro. What you type is saved automatically a moment after you stop typing.
The code you write is the body of a function — you do not write function or async yourself, and you write plain calls like await drive(50, 0), not hub.drive(...). Every name described below is already available to call.
await every call. Not only sensor reads and waits — every single one, including drive, stopDrive and lights. Each call is a request to the page that owns the radio, and the page can refuse it: a duration over the ceiling, an unsafe.* method with the checkbox off, a call from the wrong drive family. A call you did not await is a refusal with nobody listening, and the macro carries on as if it had worked.
Watch outA failure — a thrown error, or a call this page refuses — stops the macro and shows error at line N: ... in the status line underneath. That line number is the line inside this box, not some internal offset.
unsafe
What it doesA checkbox. Off by default, and off again every time a macro is imported from a file regardless of what the file says. With it off, any call to one of the unsafe.* methods described below is refused before it reaches the hub. With it on, they are allowed.
Watch outEvery unsafe.* method is here because a hardware session with this exact hub found it dangerous. Turning this on for a macro you did not write yourself, or did not read carefully, is how those lessons get relearned the hard way.
Run / Stop
What it doesRun starts the macro; Stop ends it early. Both are disabled when there is nothing to do — Run while one is already going, Stop while none is.
Watch outStarting a macro switches your gamepad to a watching mode: it keeps listening so that touching any stick or button ends the macro immediately and hands control back to you, but it commands nothing on its own while the macro is in charge. Anything else that would normally cut the motors — STOP ALL, a disconnect, a collision (see collision(mode) below), a low-signal warning from the hub — ends the macro too.
elapsed / status
Not controls. elapsed counts up in tenths of a second while a macro runs. The line underneath shows starting…, running, stopping…, the macro's own print() output, or the last result once it stops. That result is idle on a clean finish, error at line N: ... on a failure, and otherwise stopped: ... naming what ended the run — stopped: collision, stopped: low signal, stopped: disconnected, stopped: gamepad, stopped: by the Stop button. A car that stopped itself tells you which of those it was, rather than reading the same as a clean finish.
A macro that changed the drive mode with mode() also says which mode it left behind — idle — drive mode left as playvm, or stopped: low signal — drive mode left as raw — on any run that ends without a script error. A run that ends on an error shows error at line N: ... instead, and that line has no room for the mode, so after a failed run check the drive mode in Setup rather than the status line. The mode is not put back when the run ends, because returning to the combined frame would sweep the steering rack again, and a car that moves after a stop is worse than one left in the wrong mode. The line is there so the next thing you do knows which mode it is starting from.
While a macro is arming that mode, the same line reads arming combined frame — the steering rack will sweep…, so a few seconds of apparent stillness at the start of a run is accounted for.
Methods
What it doesThe panel under the editor lists every method a macro can call, each with its signature and a one-line description; the ones tied to a single drive family, or to unsafe, also carry a badge saying which. Typing in the search box narrows the list, matching both the name and the description — so "brake" finds brake(port), and "battery" finds it whether you remember the name or only what it does.
Clicking a row inserts a complete, runnable call at the cursor in the Source box, ready-awaited and with plausible numbers already filled in, so a macro can be assembled without typing a name from memory. Whatever you had selected is replaced, and the change is saved like anything else you type.
Watch outThe eight unsafe.* methods are listed only while the unsafe checkbox is ticked. With it off, a line under the list tells you how many are hidden rather than pretending the list is everything there is; while you are searching it counts only the hidden ones your search matches, so ticking the box always adds what the line said it would. The inserted call is a starting point, not advice — the numbers in it are examples, and a method still has to suit the family your macro is already using.
donut / bumper / sweep
What it doesThree worked examples, above the search box. Clicking one saves it as a new macro slot and switches to it.
When you'd use itAs a first macro, or to see the shape of something before writing your own — donut is the combined frame, bumper picks its own drive mode with mode('raw') and then shows collision('stop') with waitFor, backing off with tankFor to spin on the spot, and sweep is the raw path with a sensor read.
Watch outEach example arrives with unsafe off, like any new macro. bumper opens with mode('raw'), so it runs from a cold start whatever the hub happened to be doing. sweep does not: it drives raw motors without asking for the mode first, so either give it the same opening line or leave the hub out of its combined-frame drive mode before you run it. donut is the other way round and needs that mode — the one the app enters on its own when you connect, but not one donut arms for itself if you have since left it.
Export / Import
What it doesExport downloads every saved macro as one JSON file. Import reads a file of that shape back in, adding to what you already have — a macro sharing an id with one you already have replaces it, and everything else is left alone.
Watch outEvery macro coming in from Import lands with unsafe off, no matter what the file says it should be. If it needs unsafe calls, you turn that on yourself, deliberately, after looking at what you are importing.
The macro language
Two drive families exist, the same two the Drive tab's drive-behaviour dropdown chooses between, and a macro uses one of them at a time:
- The combined frame — the hub's own drive mode (
playvmbelow):drive,driveFor,stopDrive,lights. These work while the hub is actually in that drive mode, which the macro can enter for itself withmode('playvm')instead of your setting it on the Drive tab first. - Raw motors — direct per-motor control:
motorFor,throttleFor,tankFor,brake,brakeAll,coast,steer,steerZero. These work while the hub is not in combined-frame mode, which the macro can arrange for itself withmode('raw').
Whichever family your macro's first motion call belongs to, every later call is held to it until either the run ends or mode() changes the drive mode: a successful mode() closes that stretch of the run and opens a new one, free to use the other family. Mixing the two inside one stretch — calling a raw-path method while the combined frame is armed, or the other way around — is still refused. Everything else — sensors, lights, waits, collision settings, print — works under either family and does not choose one for you.
The two directions are not equally available, and it is the one thing worth remembering about mode(). Going to 'raw' is allowed at any point in a run. Going to 'playvm' is allowed only before the run's first motion call, because arming the combined frame sweeps the steering rack and that sweep is itself movement — a car already driving under the macro must not start calibrating its own steering halfway through. A mode('raw') in between does not buy the chance back: the run has moved, and that is what closed it.
Because arming is what sweeps the rack, a raw macro and a combined-frame macro run one after the other sweep it once each. Every entry into the hub's own drive mode calibrates the steering again, whether a macro asked for it or you chose it from the Drive tab, so alternating the two is a few seconds of rack movement each time round. That is the hub doing what it always does on arming, not something going wrong.
Every motion call that takes a duration is capped at ten seconds; asking for longer is refused before anything moves.
Sensor reads (tilt, accel, battery, motorSpeed, motorPos) and waitFor('collision') all take a timeout in milliseconds, three seconds by default. The underlying stream only reports when its value actually changes, so a sensor sitting at a steady reading produces nothing to read — measured on hardware at 18 seconds of silence with not one byte back. So the first read of a sensor in a run has to wait for a sample to arrive, and rather than hang forever, or hand back a made-up zero, a first read that outlasts its timeout fails with a message saying so.
Once a sensor has produced its first sample, the page keeps listening to it for the rest of the run and every later read of that sensor answers immediately with the newest value the hub has sent. That is what makes await waitUntil(async () => await motorPos(port) > 90) work: each pass of the loop sees a fresh reading, not the one the first read happened to catch.
drive(speed, steer)
What it doesCommands the hub's combined frame: speed and steering as one instruction, exactly like the Drive tab's default drive mode. The command keeps being refreshed on its own — you do not have to repeat it — for as long as ten seconds, after which it releases itself even if you never call stopDrive().
Path / unsafe. Combined frame (playvm) · not unsafe.
driveFor(speed, steer, ms)
What it doesSame as drive, but releases on its own after ms milliseconds (ten seconds at most) instead of waiting for stopDrive().
Path / unsafe. Combined frame (playvm) · not unsafe.
stopDrive()
What it doesReleases a drive/driveFor command early.
Path / unsafe. Combined frame (playvm) · not unsafe.
lights(mode)
What it doesSets the combined frame's lights field. mode is one of 'both', 'off', 'brake' or 'front-off-brake'; anything else is refused.
Path / unsafe. Combined frame (playvm) · not unsafe.
motorFor(port, speed, ms)
What it doesDrives one motor at speed for ms milliseconds (ten seconds at most), then floats it. port is one of the numbers shown in the Hub tab's Discovered ports list.
Path / unsafe. Raw motors · not unsafe.
throttleFor(speed, ms)
What it doesDrives both drive motors together at the same speed for ms milliseconds, then floats them.
Path / unsafe. Raw motors · not unsafe.
tankFor(left, right, ms)
What it doesDrives the two drive motors at independent left and right speeds for ms milliseconds, then floats both — opposite signs turn the model on the spot, the way a tracked vehicle steers.
Path / unsafe. Raw motors · not unsafe.
brake(port)
What it doesBrakes one motor — the staged coast-then-brake behaviour behind the Drive tab's "on release" setting, not an instant stop from speed.
Path / unsafe. Raw motors · not unsafe.
brakeAll()
What it doesBrakes both drive motors together.
Path / unsafe. Raw motors · not unsafe.
coast(port)
What it doesFloats one motor immediately — no braking, the motor just stops resisting.
Path / unsafe. Raw motors · not unsafe.
steer(input)
What it doesSets a steering target, -100 to 100 across the configured max angle. Refused unless the steering is in its own Steer mode, chosen from the mode dropdown at the top of the Drive tab — a macro cannot switch that one itself, so set it there first. mode() covers the drive families, not the steering, and this call says so rather than quietly commanding nothing. There is no duration on this call: a closed loop is already holding the angle against feedback, not spinning a motor freely, so there is nothing here for a deadline to bound. Ending the run still floats the steer port.
Path / unsafe. Raw motors · not unsafe.
steerZero()
What it doesRecords the steering motor's current position as straight ahead — the same thing Set zero does in the Setup tab.
Path / unsafe. Raw motors · not unsafe.
steerPos()
What it doesReads the steer motor's current position in degrees, right now — not a subscription, so it has no timeout.
Path / unsafe. Works under either drive family · not unsafe.
lamps(mask, brightness)
What it doesSets the six lamps directly. mask is a bit per lamp — bit 0 is Lamp 1 through bit 5 for Lamp 6, so 0x3f is all six — and brightness is 0 to 100.
Path / unsafe. Works under either drive family · not unsafe.
led(colour)
What it doesSets the status LED. colour is the same numeric code the Lights tab's buttons use: 9 red, 6 green, 3 blue, 7 yellow, 0 off.
Path / unsafe. Works under either drive family · not unsafe.
tilt(timeoutMs)
What it doesReturns the latest tilt reading as {x, y, z}. See "The macro language" above for what the first read of a steady, unchanging sensor does to this call.
Path / unsafe. Works under either drive family · not unsafe.
accel(timeoutMs)
What it doesReturns the latest accelerometer reading, in mG, as {x, y, z}.
Path / unsafe. Works under either drive family · not unsafe.
battery(timeoutMs)
What it doesReturns the latest battery reading as a percentage.
Path / unsafe. Works under either drive family · not unsafe.
motorSpeed(port, timeoutMs)
What it doesReturns the latest speed reading from one motor's port. A steady speed, including a steady zero, is a value that never changes — see "The macro language" above for why the first read of one can time out.
Path / unsafe. Works under either drive family · not unsafe.
motorPos(port, timeoutMs)
What it doesReturns the latest position reading from one motor's port, in degrees.
Path / unsafe. Works under either drive family · not unsafe.
ports()
What it doesReturns the same port map the Hub tab's Discovered ports list shows — which numeric port is which motor, lamp or sensor role.
Path / unsafe. Works under either drive family · not unsafe.
wait(ms)
What it doesPauses the macro for ms milliseconds and does nothing else. This is how a macro paces itself between steps.
Path / unsafe. Works under either drive family · not unsafe.
waitUntil(predicate, timeoutMs, pollMs)
What it doesCalls your predicate function repeatedly — every pollMs milliseconds, 50ms by default — until it returns something truthy, then returns true. timeoutMs (three seconds by default) bounds the whole wait; running out of time is a failure, not a false result. Your predicate can call any of the other functions here, including sensor reads.
Path / unsafe. Works under either drive family · not unsafe.
waitFor(name, timeoutMs)
What it doesWaits for an event. The only name supported is 'collision', which resolves the moment a hit is detected. Anything else is refused immediately rather than left to wait for an event that will never come.
Watch outRefused outright while collision(mode) (below) is still 'abort', because under that mode the hit this call is waiting for is also the hit that ends the run — the call could never return. Call collision('stop') or collision('notify') first if you want to react to a bump instead of having it end the macro. It also only ever sees a hit the crash guard actually detected, so the Hub tab's guard has to be armed and the knock has to clear collisionThreshold(mg).
Path / unsafe. Works under either drive family · not unsafe.
mode(name)
What it doesChanges the hub's drive behaviour from inside the macro, so a run can put the car into the family it needs rather than depending on what the Drive tab was left showing. name is one of two:
'playvm'arms the hub's own combined frame — the same thing choosing Typical module behaviour in the Drive tab does, and it sweeps the steering rack in the same way, taking a few seconds. The status line saysarming combined frame — the steering rack will sweep…before it starts, in the same words the Drive tab uses.'raw'hands the motors back to this page, which is what the raw-motor methods need.
Asking for the mode the hub is already in does nothing at all, so await mode('raw'); at the top of a raw macro costs nothing on a car that was already out of the combined frame — and, in particular, does not disturb an Independent or Tracked setup you chose yourself.
Watch out'playvm' has to come before the macro moves anything. Once any motion call has run, arming is refused for the rest of that run, and a mode('raw') in between does not give the chance back — see "The macro language" above for why. 'raw' has no such restriction.
Watch outAwait it, like everything else. The switch takes seconds, and any other call made while it is still going is refused saying so. If the hub will not arm, the page falls back to a mode that drives the motors directly and the call fails, rather than leaving the macro to drive a frame that was never armed. Arming also drops the steering out of Steer back to Raw, exactly as choosing the mode by hand does, so steer() is unavailable afterwards until that dropdown is set again — mode() chooses between the drive families, and steering's own mode is not one of them.
Path / unsafe. Works under either drive family · not unsafe.
collision(mode)
What it doesChanges what a detected impact does to this run. This only matters while the Hub tab's crash guard is armed — that toggle is what actually watches the accelerometer; this call only changes what happens once it reports a hit. Restored to whatever it was before your macro touched it once the run ends.
mode is one of four:
'abort'— the default. The motors are cut and the run ends.'stop'— the motors are cut and the run keeps going, so awaitFor('collision')in your macro is what hears about the bump. This is the mode for a bumper-bot: drive, wait for the knock, back off, carry on.'notify'— nothing is cut at all. The run keeps going and keeps driving, and only yourwaitFor('collision')reacts.'off'— the hit is ignored entirely.
Watch out'stop' cuts the motors every time, including the steering, so a macro reacting to the bump has to re-command whatever it wants moving. And 'notify' means a crash does nothing on its own — the car keeps driving into whatever it hit unless your macro stops it.
Path / unsafe. Works under either drive family · not unsafe.
collisionThreshold(mg)
What it doesChanges the mG threshold that counts as a hit, for the duration of the run — 1800 mG by default, which is a real crash and far too deaf for a bumper. Lowering it also re-tunes the hub's own filtering to match, because the hub, not this page, is what decides which readings are worth sending; a threshold change that did not move that filter would be invisible. Restored afterwards, subscription included, the same as collision(mode).
Watch outA low threshold means more accelerometer traffic on the link for the whole run, and hard driving over a bump starts registering as a crash. Whichever value you pick, the crash guard on the Hub tab still has to be armed for any of it to fire.
Path / unsafe. Works under either drive family · not unsafe.
print(...args)
What it doesWrites its arguments, joined with a space, to the status line below the Run/Stop buttons — the macro's own equivalent of a console log, since there is no console here.
Watch outCapped at 20 calls a second. Past that, further calls in the same second are silently dropped and one line is written to the Debug log saying so, rather than flooding the status line.
Path / unsafe. Works under either drive family · not unsafe.
unsafe.raw(bytes, key)
What it doesSends an arbitrary byte array as a raw LWP frame — the same thing the Debug tab's port probe does, from code instead of by hand.
Path / unsafe. Works under either drive family · unsafe.
unsafe.writeDirect(port, mode, values)
What it doesSends a WriteDirectModeData frame to any port, mode and value list you choose.
Path / unsafe. Works under either drive family · unsafe.
unsafe.subscribe(port, mode, delta)
What it doesSubscribes to an arbitrary port and mode. Still goes through the same one-mode-per-port bookkeeping every other subscription in this app does, and is released automatically when the run ends even if you never call unsafe.unsubscribe yourself.
Path / unsafe. Works under either drive family · unsafe.
unsafe.unsubscribe(port, mode)
What it doesReleases a subscription started with unsafe.subscribe.
Path / unsafe. Works under either drive family · unsafe.
unsafe.gotoPosition(port, angle, speed, maxPower, endState)
What it doesSends GotoAbsolutePosition.
Watch outThis is known to crash this hub's firmware when sent to the steer motor. It exists for deliberate probing, not for driving.
Path / unsafe. Works under either drive family · unsafe.
unsafe.speedForDegrees(port, degrees, speed, maxPower, endState)
What it doesSends StartSpeedForDegrees — the same suspect command family as unsafe.gotoPosition, kept for probing only.
Path / unsafe. Works under either drive family · unsafe.
unsafe.linkDriveMotors()
What it doesFuses the two drive motors into a hub-side virtual port.
Watch outThis has reliably knocked this hub off the air within 0.1 to 1.1 seconds in every session that tried it. It is kept only so the failure is reachable for anyone who needs to confirm it again; it is not a shortcut for linked drive.
Path / unsafe. Works under either drive family · unsafe.
unsafe.unlinkDriveMotors()
What it doesReverses unsafe.linkDriveMotors().
Path / unsafe. Works under either drive family · unsafe.
Debug tab
You will not normally need this tab, and it is the only place that can write whatever you type straight to the hub.
Debug log
Not a control. A running list of everything the page sends and receives, plus its own notes.
Copy
What it doesPuts the log on the clipboard, so you can paste it into a bug report.
Clear
What it doesEmpties the log.
Copy writes the whole log to the clipboard and notes that it did so as a new line at the bottom, so you can tell it worked. Clear empties the panel only — it does not tell the hub anything and does not affect what is being recorded next.
Raw port probe (fold-out, "can drop the hub connection")
What it doesSends arbitrary instructions to an arbitrary part of the hub. It exists because that is how this page's authors worked out what the hub responds to.
- port 0x — which part of the hub to address, written in hexadecimal. Starts at
35. - mode — which of that part's several behaviours to use. Whole number, 0 to 10, starting at 0.
- value — the number to send. 0 to 255, starting at 100.
- Send — sends port, mode and value as written.
- Send 0 — sends the same port and mode with a value of zero, which is usually the "off" or "stop" case.
- Sweep modes on port — sends the value at modes 0 through 5 in turn, about a second apart, switching off between each, and writes each step to the log. Useful for finding out what a port responds to.
- multi-value (csv) — a list of numbers separated by commas, for instructions that take several values at once. Starts at
100,0,0,0,0,0. - Send CSV — sends that list to the port and mode above. Anything in the list that is not a number is dropped.
Watch outThe heading says it plainly: these writes can drop the hub connection, and the hub may need a power cycle afterwards. There is no confirmation step and no undo. Do not use this with a model on the floor.
Port introspection — read-only (fold-out)
What it doesAsks a port what it is and what it can report, without ever writing a value to it. This is the safe half of the probe: everything here is a question.
- port 0x — which part of the hub to ask about, in hexadecimal. Starts at
3e. - mode — which of that part's behaviours to watch. Whole number, 0 to 10.
- delta — how much a value must change before the hub reports it again. 1 or more.
- Describe port — asks the port how many modes it has, then walks every one of them asking for its name, its range and its unit, writing each answer to the log. Takes a couple of seconds.
- Read value once — asks the port for its current value and logs the reply.
- Watch mode — subscribes to the port at the mode and delta above, so every change is logged as it happens.
- Stop watching — ends that subscription.
Watch outA delta of 0 would ask the hub to report continuously and has been seen to take the link down. The page refuses it and says so.
Watch outA port streams one mode at a time. Watching a port that the Drive tab's Measured, the Motion tab or the steering is already using takes the stream away from them, and they will report it as unavailable. Stop watching to give it back.
When something is wrong
It will not connect. Check the browser first — this needs Web Bluetooth, which Chrome and Edge have on Android, macOS, Windows and ChromeOS, and which Safari and Firefox do not have at all. On an iPhone or iPad every browser uses Safari's engine, so you need a Web Bluetooth browser such as Bluefy. If the browser is right, the message under the Connect button and the full text in the Hub tab say what went wrong. Make sure the hub is charged and switched on.
Nothing moves. Work down this list.
- Is the pad button in the top bar showing ARMED? If it says OFF, the gamepad is not driving anything. Press it.
- Does the pad readout beside it say "not connected"? Press a button on the controller — browsers only reveal a pad after it has been used.
- Check max speed in Setup. At its lowest setting the car is very slow, not stopped, but it can look like nothing is happening on carpet.
- Check deadzone in Setup. Set very high, small stick movements are ignored entirely.
- Look at the battery reading in the top bar, and at the
qnumber beside it. Aqnumber that climbs and stays up means the link is saturated. Turn telemetry and crash guard off in the Hub tab. - Try FWD on a motor row in the Drive tab. If that moves the model and the gamepad does not, the problem is in the pad, not the hub.
The car stops by itself. Several things stop it deliberately.
- crash guard in the Hub tab cuts the motors on a sharp impact. If it is ON, a hard bump or a drop onto a table is enough.
- Losing the gamepad stops the car — a flat controller, a Bluetooth dropout, or the pad going to sleep.
- Hiding the browser tab or switching to another app stops the car.
- Any error inside the control loop stops the car.
- If the steering was in Steer mode and lost its position feedback, it drops back to Raw on its own. Check the mode dropdown in the Drive tab — if it changed without you touching it, that is what happened.
The wheels turn the wrong way. Which fix depends on what is wrong.
- The whole car drives backwards, or one drive motor fights the other: use Motor direction in the Setup tab and set the offending motor to REVERSED. That setting is remembered.
- Steering goes left when you push right: tick invert steering direction under Advanced steering in the Setup tab.
- The wheels are straight on screen but crooked in reality: jog them straight with Steer jog in the Setup tab, then press Set zero.
- In Tracked mode, turning and throttle come out swapped: one drive motor must be set to REVERSED. The live readout on the Drive tab says so when it detects this.
Nothing matches. Try a word from the label on screen — “brake”, “trim”, “telemetry”.