diff --git a/Pipfile b/Pipfile index 576ddbf..0430d51 100644 --- a/Pipfile +++ b/Pipfile @@ -16,4 +16,4 @@ uvicorn = "*" python_version = "3.12" [scripts] -dev = 'watchfiles "./dev.py /dev/ttyACM0 src reset follow"' +dev = "./dev.py" diff --git a/src/main.py b/src/main.py index 23e02f4..8c7cd8d 100644 --- a/src/main.py +++ b/src/main.py @@ -49,6 +49,7 @@ def main(): if last_msg: try: data = json.loads(last_msg) + print(data) defaults = data.get("d", {}) bar = data.get(settings.get("name"), {}) @@ -62,6 +63,7 @@ def main(): patterns.n1 = bar.get("n1", defaults.get("n1", patterns.n1)) patterns.n2 = bar.get("n2", defaults.get("n2", patterns.n2)) patterns.n3 = bar.get("n3", defaults.get("n3", patterns.n3)) + patterns.n4 = bar.get("n4", defaults.get("n4", patterns.n4)) patterns.step = bar.get("s", defaults.get("s", patterns.step)) # Only execute pattern if it's a beat message diff --git a/src/patterns.py b/src/patterns.py index 003316d..69e5d71 100644 --- a/src/patterns.py +++ b/src/patterns.py @@ -13,6 +13,7 @@ class Patterns(PatternBase): # Inherit from PatternBase self.n1 = 0 # Default start of fill range self.n2 = self.num_leds - 1 # Default end of fill range self.n3 = 1 # Default step factor + self.n4 = 0 self.oneshot = False # New: One-shot flag for patterns like fill_range self.patterns = { "on": self.on, @@ -25,6 +26,7 @@ class Patterns(PatternBase): # Inherit from PatternBase "rainbow": self.rainbow, "specto": self.specto, "radiate": self.radiate, + "segmented_movement": self.segmented_movement, # Shortened pattern names for optimized JSON payloads "o": self.off, "f": self.flicker, @@ -35,6 +37,7 @@ class Patterns(PatternBase): # Inherit from PatternBase "r": self.rainbow, "s": self.specto, "rd": self.radiate, + "sm": self.segmented_movement, } self.step = 0 @@ -286,6 +289,112 @@ class Patterns(PatternBase): # Inherit from PatternBase self.run = False return self.delay + def segmented_movement(self): + """ + Segmented movement pattern that alternates forward and backward. + + Parameters: + n1: Number of LEDs per segment + n2: Spacing between segments (currently unused) + n3: Forward movement steps per beat + n4: Backward movement steps per beat + + Movement: Alternates between moving forward n3 steps and backward n4 steps each beat. + """ + try: + # Get parameters + segment_length = max(1, int(self.n1)) if hasattr(self, 'n1') else 3 + segment_spacing = max(0, int(self.n2)) if hasattr(self, 'n2') else 2 + forward_step = max(0, int(self.n3)) if hasattr(self, 'n3') else 1 + backward_step = max(0, int(self.n4)) if hasattr(self, 'n4') else 0 + + # Initialize position tracking if not exists + if not hasattr(self, '_sm_position'): + self._sm_position = 0 + self._sm_last_step = -1 + + # Check if this is a new beat (step changed) + if self.step != self._sm_last_step: + # Alternate between forward and backward movement + if self.step % 2 == 0: + # Even steps: move forward (if n3 > 0) + if forward_step > 0: + self._sm_position += forward_step + direction = "FWD" + elif backward_step > 0: + # If no forward, still move backward + self._sm_position -= backward_step + direction = "BWD" + else: + direction = "NONE" + else: + # Odd steps: move backward (if n4 > 0) + if backward_step > 0: + self._sm_position -= backward_step + direction = "BWD" + elif forward_step > 0: + # If no backward, still move forward + self._sm_position += forward_step + direction = "FWD" + else: + direction = "NONE" + + # Wrap position around strip length + strip_length = self.num_leds + segment_length + self._sm_position = self._sm_position % strip_length + + # Update last step + self._sm_last_step = self.step + + # DEBUG: Print every beat + if self.step % 5 == 0: + print(f"SM: step={self.step}, dir={direction}, n3={forward_step}, n4={backward_step}, pos={self._sm_position}") + + # Clear all LEDs + self.fill((0, 0, 0)) + + # Get color + color = self.apply_brightness(self.colors[0]) + + # Calculate segment width (segment + spacing) + segment_width = segment_length + segment_spacing + + # Draw multiple segments across the strip + if segment_width > 0: + base_position = int(self._sm_position) % segment_width + + # Draw segments starting from base_position + current_pos = base_position + while current_pos < self.num_leds: + # Draw segment from current_pos to current_pos + segment_length + segment_end = min(current_pos + segment_length, self.num_leds) + for i in range(max(0, current_pos), segment_end): + self.n[i] = color + + # Move to next segment position + current_pos += segment_width + + # Handle wrap-around: draw segments that start before 0 + wrap_position = base_position - segment_width + while wrap_position > -segment_length: + if wrap_position < 0: + # Partial segment at start + segment_end = min(wrap_position + segment_length, self.num_leds) + for i in range(0, segment_end): + self.n[i] = color + wrap_position -= segment_width + + self.n.write() + return self.delay + + except Exception as e: + # DEBUG: Print error + print(f"SM Error: {e}") + # If anything goes wrong, turn off LEDs and return + self.fill((0, 0, 0)) + self.n.write() + return self.delay + if __name__ == "__main__": import time diff --git a/src/settings.py b/src/settings.py index 2100b25..c178dd3 100644 --- a/src/settings.py +++ b/src/settings.py @@ -13,8 +13,8 @@ class Settings(dict): else: self.color_order = (1, 3, 5) def set_defaults(self): - self["led_pin"] = 4 - self["num_leds"] = 59 + self["led_pin"] = 10 + self["num_leds"] = 119 self["color_order"] = "rgb" self["name"] = f"103"