> For the complete documentation index, see [llms.txt](https://docs.anthriq.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.anthriq.com/bxi-studio/developer/overview/devices/instinct/motors.md).

# Position the headset

> **Applies to:** [Instinct](/bxi-studio/developer/overview/devices/instinct.md) only. xSys electrodes are wired by hand and the device declares no `motors` feature, so check capabilities before calling. Not available on [xBud](/bxi-studio/developer/overview/devices/xbud.md).

Motors adjust electrode height so each contact seats against the scalp. Position the headset before measuring impedance, then re-check contact after each adjustment.

The `motors` feature controls the actuators. It is distinct from the `motor` register, which holds motor configuration. See [Configure channels](/bxi-studio/developer/overview/features/channels.md).

## Read motor state

`read` accepts `motor_id` for one motor or `motor_ids` for several. Both return a `results` array.

{% tabs %}
{% tab title="Node.js" %}

```typescript
const response = await client.features.executeOperation("motors", "read", {
  motor_ids: [0, 1],
});

for (const motor of response.data.results) {
  console.log(motor.motor_id, motor.enabled, motor.current_position);
}
```

{% endtab %}

{% tab title="Python" %}

```python
response = await client.execute_operation("motors", "read", {"motor_ids": [0, 1]})

for motor in response.data["results"]:
    print(motor["motor_id"], motor["enabled"], motor["current_position"])
```

{% endtab %}
{% endtabs %}

Motor 0 is enabled and sits at 12 ticks; motor 1 at 8.

| Field              | Type     | Description               |
| ------------------ | -------- | ------------------------- |
| `motor_id`         | `number` | Motor identifier          |
| `enabled`          | `number` | `0` disabled, `1` enabled |
| `current_position` | `number` | Position in ticks         |

## Move a motor

{% tabs %}
{% tab title="Node.js" %}

```typescript
const response = await client.features.executeOperation("motors", "move", {
  motor_id: 0,
  displacement: 5,
  operation: "forward",
});

console.log(response.data.current_position, response.data.status);
```

{% endtab %}

{% tab title="Python" %}

```python
response = await client.execute_operation("motors", "move", {
    "motor_id": 0,
    "displacement": 5,
    "operation": "forward",
})

print(response.data["current_position"], response.data["status"])
```

{% endtab %}
{% endtabs %}

Position 17 after moving 5 from 12, and `status` 0 meaning no fault. A stalled motor would report `1`; a stall plus gear slip would report `5`.

`operation` is `"forward"` or `"backward"`, and defaults to `"forward"`.

`displacement` is a 7-bit register field, so 0 to 127 is representable. Values above 20 are accepted but exceed the typical limit of physical travel, so the SDK warns rather than rejecting: the register allows it, the actuator may not.

> **Warning:** Position is clamped at the travel limits. A move past the end reports success while leaving the position unchanged, so read the position back rather than assuming the move landed.

## Stop, brake, and calibrate

| Operation   | Payload        | Effect                                           |
| ----------- | -------------- | ------------------------------------------------ |
| `stop`      | `{ motor_id }` | Halt movement, holding position                  |
| `brake`     | `{ motor_id }` | Apply the brake                                  |
| `calibrate` | `{ motor_id }` | Home the motor and reset `current_position` to 0 |

{% tabs %}
{% tab title="Node.js" %}

```typescript
await client.features.executeOperation("motors", "stop", { motor_id: 0 });
await client.features.executeOperation("motors", "brake", { motor_id: 0 });
```

{% endtab %}

{% tab title="Python" %}

```python
await client.execute_operation("motors", "stop", {"motor_id": 0})
await client.execute_operation("motors", "brake", {"motor_id": 0})
```

{% endtab %}
{% endtabs %}

Calibration homes the motor against its end stop and can exceed the default 30-second timeout. Override it for that call:

{% tabs %}
{% tab title="Node.js" %}

```typescript
await client.invoke({
  feature: "motors",
  operation: "calibrate",
  payload: { motor_id: 0 },
  timeoutMs: 120000,
});
```

{% endtab %}

{% tab title="Python" %}

```python
await client.invoke({
    "feature": "motors",
    "operation": "calibrate",
    "payload": {"motor_id": 0},
    "timeoutMs": 120000,
})
```

{% endtab %}
{% endtabs %}

All four movement operations return `motor_id`, `current_position`, and `status`.

## Check for faults

`status` is a 3-bit field, and several bits can be set at once. Test the bits rather than comparing the whole value.

| Flag                          | Value | Meaning               |
| ----------------------------- | ----- | --------------------- |
| `MotorStatus.Stall`           | `1`   | Motor stalled         |
| `MotorStatus.NotResponding`   | `2`   | Motor not responding  |
| `MotorStatus.GearSlipOverrun` | `4`   | Gear slip or over-run |

`0` means no fault.

{% tabs %}
{% tab title="Node.js" %}

```typescript
import { MotorStatus, motorStatusToString } from "@anthriq_dev/bxi-interface";

const status = response.data.status;

if (status & MotorStatus.Stall) {
  await client.features.executeOperation("motors", "stop", { motor_id: 0 });
  console.warn(motorStatusToString(status));
}
```

{% endtab %}

{% tab title="Python" %}

```python
from anthriq_bxi_interface import MotorStatus, motor_status_to_string

status = response.data["status"]

if status & MotorStatus.STALL:
    await client.execute_operation("motors", "stop", {"motor_id": 0})
    print(motor_status_to_string(status))
```

{% endtab %}
{% endtabs %}

> **Warning:** A stalled motor that is not stopped keeps drawing current against the obstruction. Check `status` after every move and stop the motor when a fault bit is set.

## Verify a move

Position is reported after the move completes, but reading it back confirms the actuator reached the target rather than clamping.

{% tabs %}
{% tab title="Node.js" %}

```typescript
async function readPosition(motorId: number): Promise<number> {
  const response = await client.features.executeOperation("motors", "read", {
    motor_ids: [motorId],
  });
  return response.data.results[0].current_position;
}

const before = await readPosition(0);

await client.features.executeOperation("motors", "move", {
  motor_id: 0,
  displacement: 5,
  operation: "forward",
});

await new Promise((resolve) => setTimeout(resolve, 500));

const after = await readPosition(0);
const expected = Math.min(20, before + 5);

if (Math.abs(after - expected) > 1) {
  console.warn(`Position mismatch: expected about ${expected}, read ${after}`);
}
```

{% endtab %}

{% tab title="Python" %}

```python
import asyncio

async def read_position(motor_id: int) -> int:
    response = await client.execute_operation("motors", "read", {"motor_ids": [motor_id]})
    return response.data["results"][0]["current_position"]

before = await read_position(0)

await client.execute_operation("motors", "move", {
    "motor_id": 0,
    "displacement": 5,
    "operation": "forward",
})

await asyncio.sleep(0.5)

after = await read_position(0)
expected = min(20, before + 5)

if abs(after - expected) > 1:
    print(f"Position mismatch: expected about {expected}, read {after}")
```

{% endtab %}
{% endtabs %}

## Seat electrodes against impedance

Positioning and contact measurement are one loop: adjust, re-read impedance, repeat until the contact is good.

{% tabs %}
{% tab title="Node.js" %}

```typescript
// Impedance stream already running; see Measure contact impedance.
for (let attempt = 0; attempt < 5; attempt++) {
  const quality = await readContactQuality(channelId);
  if (quality === "good") break;

  await client.features.executeOperation("motors", "move", {
    motor_id: motorForChannel(channelId),
    displacement: 2,
    operation: "forward",
  });

  await new Promise((resolve) => setTimeout(resolve, 1000));
}
```

{% endtab %}

{% tab title="Python" %}

```python
import asyncio

# Impedance stream already running; see Measure contact impedance.
for _ in range(5):
    quality = await read_contact_quality(channel_id)
    if quality == "good":
        break

    await client.execute_operation("motors", "move", {
        "motor_id": motor_for_channel(channel_id),
        "displacement": 2,
        "operation": "forward",
    })

    await asyncio.sleep(1)
```

{% endtab %}
{% endtabs %}

Which motor serves which channel comes from the Synap register's `associated_motor_id`, set when `motor_associated` is `1`.

## Next steps

* [Measure contact impedance](/bxi-studio/developer/overview/features/impedance.md)
* [Configure channels](/bxi-studio/developer/overview/features/channels.md)
* [Stream EXG](/bxi-studio/developer/overview/features/exg.md)
