Add segmented_movement pattern with alternating forward/backward movement
- Add n4 parameter support to main.py ESP NOW receiver - Implement segmented_movement pattern with configurable parameters: * n1: segment length (number of LEDs per segment) * n2: spacing between segments * n3: forward movement speed (positions per beat) * n4: backward movement speed (positions per beat) - Pattern alternates between forward and backward movement each beat - If only n3 or n4 is set, moves in that direction every beat - Draws repeating segments with spacing across entire LED strip - Add Pipfile script to run dev.py directly with arguments
This commit is contained in:
2
Pipfile
2
Pipfile
@@ -16,4 +16,4 @@ uvicorn = "*"
|
||||
python_version = "3.12"
|
||||
|
||||
[scripts]
|
||||
dev = 'watchfiles "./dev.py /dev/ttyACM0 src reset follow"'
|
||||
dev = "./dev.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
|
||||
|
109
src/patterns.py
109
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
|
||||
|
@@ -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"
|
||||
|
||||
|
Reference in New Issue
Block a user