A physical, robotic Newton's Cradle exhibit. Visitors use a touchscreen to choose how many balls to pull back and release on each side; motorized "scooper" arms grab, lift, and release the selected balls to demonstrate conservation of momentum.
- Raspberry Pi running the Kivy touchscreen app.
- 4 stepper motors across two DPiStepper boards (
dpeaDPilibrary):- Board 0 = right arm, Board 1 = left arm.
- On each board: stepper 0 = horizontal axis, stepper 1 = vertical axis.
- Home/limit switches on each axis, used by the homing routine.
The app runs as a systemd service (newtons-cradle.service) that launches
main.py at boot and restarts it automatically if it ever exits or crashes.
Install on the Pi (repo cloned to /home/pi/Newtons-Cradle):
# One-time: create this machine's live tuning file from the template.
cp /home/pi/Newtons-Cradle/variables.example.json /home/pi/Newtons-Cradle/variables.json
sudo cp /home/pi/Newtons-Cradle/newtons-cradle.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now newtons-cradle.servicevariables.json holds this exhibit's live tuning (offsets + cooldown) and is
written by the admin page, so it is git-ignored — each machine keeps its own
and git pull never overwrites it. variables.example.json is the tracked
default to copy from.
Useful commands:
systemctl status newtons-cradle.service # is it running?
journalctl -u newtons-cradle.service -f # live logs
sudo systemctl restart newtons-cradle.servicemain.py loads its .kv files and AdminScreen using paths relative to the
repo root, so it must be run with the repo root as the working directory (the
service sets WorkingDirectory=/home/pi/Newtons-Cradle).
Note: SDL logs harmless
EVDEV KeyCode 330 ... not recognized by SDLmessages — that code is the touchscreen'sBTN_TOUCHevent, which has no keyboard mapping. It has no effect on the app; the service filters these lines out of the logs.
| File | Purpose |
|---|---|
main.py |
Kivy app: touchscreen UI, ball gesture logic, scoop orchestration. Entry point. |
stepper_hardware.py |
Hardware API over the two DPiStepper boards (init_hardware, scoop, home, double_home, stop_balls, ...). |
moveBothToHome.py |
Homing routines against the axis home switches. |
Kivy/ |
.kv layouts, AdminScreen, and images. |
variables.json |
Per-machine live tuning (offsets + cooldown), written by the admin UI. Git-ignored; copied from variables.example.json. |
variables.example.json |
Tracked default template for variables.json. |
newtons-cradle.service |
systemd unit that runs the app at boot. |
test_steppers.py |
CLI for driving/testing the steppers directly on the device. |
test_ui.py |
Runs the Kivy UI with hardware stubbed out (for machines without the boards). |
scoop(num_left, num_right)— scoops the requested number of balls on each side. If either side is 5, sides are staggered to avoid a collision.stop_balls(end_at_home=True)— halts ball momentum before a re-scoop so the arms can grab cleanly.home(board)/double_home()— home the steppers.double_homehomes each arm's vertical and horizontal motors together (two at a time).quit_all()/admin_quit_all()— home, disable motors, and quit; the systemd service then restarts the app.
- Sliders/ball images limit selection so the two sides never sum to more than 5.
- Ball images change color to reflect how many are selected per side.
- Invisible admin button in the bottom-right corner. Password:
7266. - Restart — homes, disables motors, and quits the app; the systemd service restarts it automatically. (Use this to reset a misbehaving session.)
- Quit — a true shutdown: homes, disables motors, then runs
systemctl stop newtons-cradle.serviceso the app does not restart. The exhibit stays off until powered off or the service is started again. - Back — re-homes the steppers and returns to the main screen.
The +/− controls at the top of the admin screen adjust live values that are
saved to variables.json and applied immediately to the hardware:
offset_left/offset_right— horizontal trim (mm) for how far each arm reaches toward the balls. Defaults 9 (right) / −4 (left).offset_v_left/offset_v_right— vertical trim (mm) added to each arm's lift height when scooping. Defaults 0.COOLDOWN_SECS— seconds to wait between scoops.
The Quit button runs sudo systemctl stop newtons-cradle.service. For the
pi user to do that without a password prompt, add a sudoers rule (run once on
the Pi):
echo 'pi ALL=(root) NOPASSWD: /usr/bin/systemctl stop newtons-cradle.service' \
| sudo tee /etc/sudoers.d/newtons-cradle >/dev/null \
&& sudo chmod 440 /etc/sudoers.d/newtons-cradle \
&& sudo visudo -cTo bring the exhibit back after a Quit, power-cycle it or run
sudo systemctl start newtons-cradle.service.