From 8e7b04f3ae677a1b46d69a546973fd22f6f6f389 Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Tue, 2 Dec 2025 18:48:11 +0100 Subject: [PATCH] rpi update --- .gitignore | 1 + rpi/lights-off.py | 9 +++++++++ rpi/lights.py | 2 +- rpi/main.py | 20 +++++++++++++++++--- rpi/stepper-api-test.py | 4 ++++ rpi/stepper-test.py | 1 - rpi/stepper.py | 25 ++++++++++++++++--------- 7 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 rpi/lights-off.py create mode 100644 rpi/stepper-api-test.py diff --git a/.gitignore b/.gitignore index 0af181c..a741755 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Godot 4+ specific ignores .godot/ /android/ +__pycache__ diff --git a/rpi/lights-off.py b/rpi/lights-off.py new file mode 100644 index 0000000..7186111 --- /dev/null +++ b/rpi/lights-off.py @@ -0,0 +1,9 @@ +import board +import neopixel + +pixels = neopixel.NeoPixel( + board.D12, 10, brightness=1, auto_write=False, pixel_order=neopixel.GRB +) + +pixels.fill((0, 0, 0)) +pixels.show() \ No newline at end of file diff --git a/rpi/lights.py b/rpi/lights.py index 6212e15..f632447 100644 --- a/rpi/lights.py +++ b/rpi/lights.py @@ -19,7 +19,7 @@ class Lights: for i in range(pixels.n): pattern_offset = wrap((i + (self.virtual_rotation * PATTERN_REPETITION)) / PATTERN_REPETITION, 0, 1) energy = (1 - (1 - pattern_offset) * max_darkness) - new_color = tuple(x * energy for x in self.color) + new_color = tuple(int(x * energy) for x in self.color) self.pixels[i] = new_color self.pixels.show() self.expected_rotation_delta = lerp(self.expected_rotation_delta, 0, delta * LIGHT_SLOWDOWN_SPEED) diff --git a/rpi/main.py b/rpi/main.py index b8d2d08..42020b1 100644 --- a/rpi/main.py +++ b/rpi/main.py @@ -31,13 +31,22 @@ local_delta = 0 remote_rotation = 0 remote_delta = 0 +def motor_rotation_fn(): + global remote_rotation + while True: + target_pos = stepper.fpos_to_pos(1 - remote_rotation) + stepper.single_step_towards(target_pos) # blocking / time.sleep + def read_thread_fn(): global local_rotation, local_delta while True: old_rotation = local_rotation local_rotation = read_angle_f() local_delta = gdmath.shortest_diff(old_rotation, local_rotation) - send_json({"value": local_rotation, "delta": local_delta}) + try: + send_json({"value": local_rotation, "delta": local_delta}) + except Exception as e: + print("Error sending data:", e) def lights_fn(): global local_rotation, local_delta, remote_rotation, remote_delta @@ -57,10 +66,14 @@ def network_recv_fn(): while True: data = receive_json() if data: - remote_rotation = data["value"] - remote_delta = data["delta"] + if "value" in data: + remote_rotation = data["value"] + if "delta" in data: + remote_delta = data["delta"] time.sleep(0.01) +motor_thread = threading.Thread(target=motor_rotation_fn) +motor_thread.start() read_thread = threading.Thread(target=read_thread_fn) read_thread.start() lights_thread = threading.Thread(target=lights_fn) @@ -68,6 +81,7 @@ lights_thread.start() network_thread = threading.Thread(target=network_recv_fn) network_thread.start() +motor_thread.join() read_thread.join() lights_thread.join() network_thread.join() \ No newline at end of file diff --git a/rpi/stepper-api-test.py b/rpi/stepper-api-test.py new file mode 100644 index 0000000..eaa4913 --- /dev/null +++ b/rpi/stepper-api-test.py @@ -0,0 +1,4 @@ +from stepper import stepper + +for i in range(stepper.step_count): + stepper.single_step() \ No newline at end of file diff --git a/rpi/stepper-test.py b/rpi/stepper-test.py index e01ba7a..9d1f5c3 100644 --- a/rpi/stepper-test.py +++ b/rpi/stepper-test.py @@ -36,7 +36,6 @@ def cleanup(): # the meat try: - i = 0 for i in range(step_count): if i%4==0: GPIO.output( out4, GPIO.HIGH ) diff --git a/rpi/stepper.py b/rpi/stepper.py index 3302032..1fdb970 100644 --- a/rpi/stepper.py +++ b/rpi/stepper.py @@ -1,5 +1,6 @@ import RPi.GPIO as GPIO import time +import atexit class StepperMotor: pins = (17, 27, 22, 23) @@ -10,7 +11,9 @@ class StepperMotor: GPIO.setmode( GPIO.BCM ) for pin in self.pins: GPIO.setup( pin, GPIO.OUT ) + for pin in self.pins: GPIO.output( pin, GPIO.LOW ) + atexit.register(self.cleanup) def cleanup(self): for pin in self.pins: @@ -25,18 +28,19 @@ class StepperMotor: current_step = 0 def _apply_single_step(self): - if self.current_step%4==0: - self._set_pins(GPIO.HIGH, GPIO.LOW, GPIO.LOW, GPIO.LOW) - elif self.current_step%4==1: - self._set_pins(GPIO.LOW, GPIO.HIGH, GPIO.LOW, GPIO.LOW) - elif self.current_step%4==2: - self._set_pins(GPIO.LOW, GPIO.LOW, GPIO.HIGH, GPIO.LOW) - elif self.current_step%4==3: + current_step_abs = abs(self.current_step) + if current_step_abs%4==0: self._set_pins(GPIO.LOW, GPIO.LOW, GPIO.LOW, GPIO.HIGH) + elif current_step_abs%4==1: + self._set_pins(GPIO.LOW, GPIO.HIGH, GPIO.LOW, GPIO.LOW) + elif current_step_abs%4==2: + self._set_pins(GPIO.LOW, GPIO.LOW, GPIO.HIGH, GPIO.LOW) + elif current_step_abs%4==3: + self._set_pins(GPIO.HIGH, GPIO.LOW, GPIO.LOW, GPIO.LOW) time.sleep(self.step_sleep) def single_step(self): - self._apply_single_step() self.current_step += 1 + self._apply_single_step() def single_step_back(self): self.current_step -= 1 self._apply_single_step() @@ -52,10 +56,13 @@ class StepperMotor: return (self.current_step % self.step_count) / self.step_count def single_step_towards(self, target_pos): + target_pos = target_pos % self.step_count current_pos = self.pos() + if target_pos == current_pos: + return # Determine shortest direction # includes wrap-around (it's a circular motion motor) - diff = (target_pos - current_pos + self.step_count) % self.step_count + diff = (target_pos - current_pos) % self.step_count if diff > self.step_count / 2: self.single_step_back() else: