Motors

The Training Village drives standard hobby servomotors through PCA9685 PWM controllers over I2C: one chip on the corridor board (up to 4 door motors) and one on the box board (up to 7 motors). Each motor is calibrated individually and stored in its own MOTORx_VALUES setting.

Which motors exist

Motor

Enabled by

Values setting

Corridor doors 1–2

always present

MOTOR1_VALUES, MOTOR2_VALUES

Corridor doors 3–4

MOTOR3_CORRIDOR, MOTOR4_CORRIDOR

MOTOR3_VALUES, MOTOR4_VALUES

Box motors 1–7

MOTOR1_BOX … MOTOR7_BOX

MOTOR1_BOX_VALUES … MOTOR7_BOX_VALUES

The corridor motors are enabled under SETTINGS → CORRIDOR SETTINGS and the box motors under SETTINGS → BOX SETTINGS. A motor left OFF is replaced by a NullMotor, whose methods accept the same calls and do nothing, so a task that drives a disabled motor still runs without errors.


Calibration

Motor values are not edited from the SETTINGS screen. Each motor has its own MOTORx VALUES button in the MONITOR screen, under the CORRIDOR or BOX tab, which opens a dialog where the values can be changed and tested with OPEN/CLOSE before saving. See Corridor Hardware Setup for the full calibration procedure.

Each motor stores five values:

Field

Meaning

open angle

Angle (0–180°) of the open position

close angle

Angle (0–180°) of the closed position

open time (ms)

Total milliseconds the travel to open should take. 0 = as fast as the servo can move

close time (ms)

Same, for the travel to close

hold

Whether the servo keeps holding torque after arriving (see below)

Travel time is not a delay: the movement is split into 5° steps and the time is spread evenly across them, so the door glides instead of snapping. A 30° move with close time = 300 performs 6 steps of 50 ms each.


Holding torque

A powered servo actively fights anything that tries to move it. That is usually what a door wants — but it also means the motor hums continuously and warms up, and for a mechanism that does not need to resist anything, that is wasted wear and noise.

The hold value decides what happens after the motor reaches its target:

hold

Behaviour

True (checked, the default)

The PWM signal keeps running. The servo resists a load and stays exactly in place, but hums and warms up.

False (unchecked)

The PWM signal is cut once the motor has arrived. The servo goes silent and cool, but anything pushing on it can move it out of position.

Corridor doors normally want hold on, since the door must stay where it was put. A mechanism that latches mechanically, or that only needs to reach a position and then be left alone, is better off with it unchecked.

Note

When hold is off and the travel time is 0 (an instant move), the system waits one second before cutting the power, so the servo has time to physically get there — cutting it immediately would leave the motor stranded mid-travel. This wait is the MOTOR_SETTLE_MS constant in village/devices/chip.py; a heavier door with a long travel may need it raised.


Using motors in tasks

Import the module-level instance of the motor you need and call it directly:

from village.devices.chip import motor_box1, motor_corridor1

# Move to the calibrated open/close positions, using this motor's saved
# angles, times and hold value
motor_box1.open()
motor_box1.close()

# Override the saved hold value for a single movement
motor_box1.open(hold=False)   # release the servo once it is open
motor_box1.close(hold=True)   # keep this one powered, whatever the setting says

# Move to an arbitrary angle over an arbitrary time
motor_box1.move(120)            # instantly
motor_box1.move(120, 500)       # spread over 500 ms
motor_box1.move(120, 500, hold=False)

# Release holding torque at any later moment
motor_box1.disable()

Method reference

Method

Arguments

Description

open(hold=None, settle_ms=1000)

—

Moves to the calibrated open angle over the calibrated open time.

close(hold=None, settle_ms=1000)

—

Moves to the calibrated close angle over the calibrated close time.

move(angle, total_ms=0, hold=None, settle_ms=1000)

angle: 0–180, total_ms: total travel time

Moves to an arbitrary angle.

disable()

—

Cuts the PWM signal, releasing holding torque immediately.

hold=None (the default on all three movement methods) means use this motor’s saved value. Pass hold=True or hold=False to override it for that one call without changing the setting. settle_ms only has an effect when hold resolves to False.


These calls never block

Every open(), close(), move() and disable() call returns immediately. The movement itself — including the step-by-step ramp, the settle wait and cutting the power — runs on a background thread, one per PWM chip.

This matters in two ways:

  • A task, a GUI button or the corridor state machine can command a 300 ms door movement without stalling for 300 ms. Nothing waits for a servo.

  • Commands to motors on the same chip are executed strictly one at a time, in the order they were submitted. Two box motors never move simultaneously, and a close() issued right after an open() always happens after it finishes. The corridor chip and the box chip have separate threads, so they do run in parallel with each other.

Because the calls return before the movement is done, there is no return value and no way to wait for completion. If a task needs to know the door has finished, it must allow for the travel time itself (the open time/close time it was calibrated with).

Warning

A motor that was not connected when Village started is not picked up later. After plugging a motor in, or after enabling one in SETTINGS, Village must be restarted — see Troubleshooting.

Note

Systems using the legacy corridor board (OLD_VERSION enabled in SETTINGS → ADVANCED SETTINGS) drive the corridor doors straight from the Raspberry Pi’s hardware PWM instead of a PCA9685. Those motors move instantly, ignore travel times, and have no hold option: their dialog shows no Hold position checkbox, and only open() and close() are available.