Servo
Drive up to 8 PWM hobby servos from the browser — with smooth sweeps, timed moves, and angle read-back.
Requires <PardaloteServo.h> in the sketch (plus the ESP32Servo library on ESP32). Register instances before connecting; examples assume:
arduino.add('pan', new Servo());
Alternatively, the sketch can create the servo — PardaloteServo.attach("pan", 9) on the Arduino — and arduino.pan appears automatically, no add() needed: a full Servo instance, identical to the above, present before 'ready' fires. See The Arduino sketch.
attach()
Attaches the servo to a pin. Call inside on('ready').
| Parameter | Type | Description |
|---|---|---|
pin |
number | string | PWM-capable pin the servo signal wire is on. |
minPulse |
number | Optional. Pulse width at 0°, in µs (default 544). |
maxPulse |
number | Optional. Pulse width at 180°, in µs (default 2400). |
arduino.pan.attach(9);
arduino.pan.attach(9, 544, 2400); // custom pulse range
detach()
Releases the pin.
write()
Moves the servo to an angle. Cancels any in-progress sweep or timed move.
| Parameter | Type | Description |
|---|---|---|
angle |
number | Target angle, 0–180 degrees. |
writeMicroseconds()
Fine-grained control via raw pulse width.
| Parameter | Type | Description |
|---|---|---|
us |
number | Pulse width in microseconds, typically 544–2400. |
center() / min() / max()
Shorthand moves.
Move to 90°, 0°, and 180° respectively.
read()
Reads the servo's angle. Same poll-and-cache pattern as analogRead(): the first call starts a poll, later calls return the cached value. The board runs the poll (per-browser interval and threshold) and only transmits changes of threshold+ degrees.
| Parameter | Type | Description |
|---|---|---|
interval |
number | Optional. Poll interval in ms. Pass END to stop. |
threshold |
number | Optional. Minimum change worth transmitting, in degrees (0 = default: 1). Also settable via setReadInterval(ms) / setReadThreshold(degrees). |
Returns the cached angle in degrees.
What the Arduino reports depends on the platform:
- UNO R4 WiFi: the last commanded angle (the Arduino Servo library's
read()is just a getter). - ESP32: the angle decoded from the LEDC PWM duty register. Usually matches the commanded angle, but the hardware round-trip can return values slightly off, and a
write()during aread()can briefly return a transitional value.
If you just want "what did I tell the servo to do?" — use the angle property (locally cached, updated on every write()).
sweep()
Sweeps between two angles over a duration, streaming intermediate writes. Returns a promise. Any write() cancels it.
| Parameter | Type | Description |
|---|---|---|
from |
number | Start angle. |
to |
number | End angle. |
duration |
number | Sweep time in ms. |
steps |
number | Optional. Number of intermediate writes (default 50). More = smoother. |
await arduino.pan.sweep(0, 180, 2000);
writeTimed()
Moves to an angle over a set time — the Arduino interpolates on-board (smooth, no WiFi streaming) and fires done on arrival. The modern replacement for sweep(), and what lets PWM servos arrive together inside a group.
| Parameter | Type | Description |
|---|---|---|
angle |
number | Target angle, 0–180. |
duration |
number | Move time in ms. |
arduino.pan.writeTimed(120, 1500); // move to 120° over 1.5 s
arduino.pan.on('done', ({ angle }) => { /* arrived */ });
An immediate write() cancels an in-progress timed move.
gesture()
Plays an authored segment schedule — an ordered list of eased moves the Arduino runs back-to-back on its own clock (on-board, no WiFi streaming). Where writeTimed() is one eased move, a gesture is many: the primitive for expressive motion — anticipation, overshoot, holds, follow-through. Fires done (resolves whenDone()) when the last segment lands. Timing follows the shared gesture timing rules: segments start on schedule, and the last key is landed or given up on 2 s after its expected arrival.
Each segment is an object with a duration, a curve, and a displacement expressed either relatively (by) or absolutely (to):
| Field | Type | Description |
|---|---|---|
dur |
number | Segment duration in ms. |
curve |
string | Easing: 'linear', 'easeIn', 'easeOut', 'easeInOut', or 'back' (overshoot). Default 'linear'. |
by |
number | Relative displacement in degrees — the default, portable frame. |
to |
number | Absolute target angle — use in place of by. |
The reference frame is inferred (to → absolute, by → relative) or forced with opts.absolute. Relative is the default: the board captures the start angle at each segment, so a gesture needs no absolute position truth. Up to 16 segments (extras dropped with a warning); a back overshoot is re-clamped to the servo range / setLimits().
arduino.pan.gesture([
{ by: 25, dur: 250, curve: 'easeOut' },
{ by: -25, dur: 400, curve: 'easeInOut' },
{ by: 6, dur: 180, curve: 'back' }, // small overshoot settle
]);
await arduino.pan.gesture([ /* … */ ]).whenDone();
To play coordinated gestures across several actuators at once, see group.gesture().
whenDone()
Promise for the most recent timed move — resolves true on the servo's done (or immediately if no move is pending), false if it didn't arrive (a gesture that gave up, or the safety timeout — default max(duration × 2, 10000) ms; pass { timeout } or a bare number to override, 0 to wait forever). The same method exists on steppers, bus servos, and groups.
await arduino.pan.writeTimed(120, 1500).whenDone();
await arduino.pan.writeTimed(60, 1500).whenDone();
stop()
Cancels a timed move and holds the current angle.
setLimits() / clearLimits()
Soft angle limits, enforced on the Arduino — every commanded angle (browser write, sketch write, timed or group move) is clamped to the range before it reaches the servo. Same shape as setLimits on the stepper and bus servo.
| Parameter | Type | Description |
|---|---|---|
min, max |
number | Allowed angle range, 0–180. |
setHome() / home()
setHome(angle) declares the home angle — no-arg means "the current angle is home". home() snaps there; home(duration) glides there over duration ms. Default home is 90 (centre). Same pair as the stepper and bus servo.
arduino.pan.setHome(45);
await arduino.pan.home(1000).whenDone();
attached()
Asks the board whether this servo is attached. Resolves true/false (false after a 2 s timeout — dead link or unresponsive board). Query → promise, like busServo.ping(); for the cached mirror, read arduino.pan.isAttached.
if (await arduino.pan.attached()) beginSequence();
else showError('pan servo not responding');
Events
| Event | Payload | Fires when |
|---|---|---|
'change' |
{ angle } |
The angle changed by at least the threshold. |
'write' |
{ angle } |
A write is issued. |
'gesture' |
{ segments, absolute, duration } |
This browser sends a gesture (local echo). |
'gesturestart' |
{} |
The board starts playing a schedule on this servo — any origin (this browser, another, or the sketch). |
'gestureend' |
{} |
…that schedule ends — completion or a superseding write. |
'done' |
{ angle, arrived } |
A timed move or gesture finishes. arrived is false only for a gesture that gave up. |
Shorthand: onChange(fn), onWrite(fn), onDone(fn), onGestureStart(fn), onGestureEnd(fn). The board reports whether a schedule is playing (any origin) via the isGesturing property; a browser that connects mid-gesture learns it on sync.
setWriteThrottle() / setWriteThreshold()
Rate-limits outgoing writes — useful when driving the servo from mouse movement or a draw loop.
The write family paces what you send (min ms between writes; skip deltas under degrees); the setRead* family gates what you hear.
| Parameter | Type | Description |
|---|---|---|
ms |
number | Minimum ms between writes (default 20). |
degrees |
number | Minimum change in degrees to send (default 1). |
Properties and state
| Property | Description |
|---|---|
arduino.pan.angle |
Locally cached snapshot, updated on every write(). |
arduino.pan.isGesturing |
true while the board is playing a segment schedule on this servo (any origin). |
Returns a snapshot of all servo state.
See also: Groups · Bus servo · Servo example