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 |
|
Corridor doors 3–4 |
|
|
Box motors 1–7 |
|
|
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 |
|---|---|
|
Angle (0–180°) of the open position |
|
Angle (0–180°) of the closed position |
|
Total milliseconds the travel to open should take. |
|
Same, for the travel to close |
|
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:
|
Behaviour |
|---|---|
|
The PWM signal keeps running. The servo resists a load and stays exactly in place, but hums and warms up. |
|
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 |
|---|---|---|
|
— |
Moves to the calibrated open angle over the calibrated open time. |
|
— |
Moves to the calibrated close angle over the calibrated close time. |
|
|
Moves to an arbitrary angle. |
|
— |
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 anopen()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.