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"
|
python_version = "3.12"
|
||||||
|
|
||||||
[scripts]
|
[scripts]
|
||||||
dev = 'watchfiles "./dev.py /dev/ttyACM0 src reset follow"'
|
dev = "./dev.py"
|
||||||
|
@@ -49,6 +49,7 @@ def main():
|
|||||||
if last_msg:
|
if last_msg:
|
||||||
try:
|
try:
|
||||||
data = json.loads(last_msg)
|
data = json.loads(last_msg)
|
||||||
|
print(data)
|
||||||
defaults = data.get("d", {})
|
defaults = data.get("d", {})
|
||||||
bar = data.get(settings.get("name"), {})
|
bar = data.get(settings.get("name"), {})
|
||||||
|
|
||||||
@@ -62,6 +63,7 @@ def main():
|
|||||||
patterns.n1 = bar.get("n1", defaults.get("n1", patterns.n1))
|
patterns.n1 = bar.get("n1", defaults.get("n1", patterns.n1))
|
||||||
patterns.n2 = bar.get("n2", defaults.get("n2", patterns.n2))
|
patterns.n2 = bar.get("n2", defaults.get("n2", patterns.n2))
|
||||||
patterns.n3 = bar.get("n3", defaults.get("n3", patterns.n3))
|
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))
|
patterns.step = bar.get("s", defaults.get("s", patterns.step))
|
||||||
|
|
||||||
# Only execute pattern if it's a beat message
|
# 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.n1 = 0 # Default start of fill range
|
||||||
self.n2 = self.num_leds - 1 # Default end of fill range
|
self.n2 = self.num_leds - 1 # Default end of fill range
|
||||||
self.n3 = 1 # Default step factor
|
self.n3 = 1 # Default step factor
|
||||||
|
self.n4 = 0
|
||||||
self.oneshot = False # New: One-shot flag for patterns like fill_range
|
self.oneshot = False # New: One-shot flag for patterns like fill_range
|
||||||
self.patterns = {
|
self.patterns = {
|
||||||
"on": self.on,
|
"on": self.on,
|
||||||
@@ -25,6 +26,7 @@ class Patterns(PatternBase): # Inherit from PatternBase
|
|||||||
"rainbow": self.rainbow,
|
"rainbow": self.rainbow,
|
||||||
"specto": self.specto,
|
"specto": self.specto,
|
||||||
"radiate": self.radiate,
|
"radiate": self.radiate,
|
||||||
|
"segmented_movement": self.segmented_movement,
|
||||||
# Shortened pattern names for optimized JSON payloads
|
# Shortened pattern names for optimized JSON payloads
|
||||||
"o": self.off,
|
"o": self.off,
|
||||||
"f": self.flicker,
|
"f": self.flicker,
|
||||||
@@ -35,6 +37,7 @@ class Patterns(PatternBase): # Inherit from PatternBase
|
|||||||
"r": self.rainbow,
|
"r": self.rainbow,
|
||||||
"s": self.specto,
|
"s": self.specto,
|
||||||
"rd": self.radiate,
|
"rd": self.radiate,
|
||||||
|
"sm": self.segmented_movement,
|
||||||
}
|
}
|
||||||
self.step = 0
|
self.step = 0
|
||||||
|
|
||||||
@@ -286,6 +289,112 @@ class Patterns(PatternBase): # Inherit from PatternBase
|
|||||||
self.run = False
|
self.run = False
|
||||||
return self.delay
|
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__":
|
if __name__ == "__main__":
|
||||||
import time
|
import time
|
||||||
|
@@ -13,8 +13,8 @@ class Settings(dict):
|
|||||||
else: self.color_order = (1, 3, 5)
|
else: self.color_order = (1, 3, 5)
|
||||||
|
|
||||||
def set_defaults(self):
|
def set_defaults(self):
|
||||||
self["led_pin"] = 4
|
self["led_pin"] = 10
|
||||||
self["num_leds"] = 59
|
self["num_leds"] = 119
|
||||||
self["color_order"] = "rgb"
|
self["color_order"] = "rgb"
|
||||||
self["name"] = f"103"
|
self["name"] = f"103"
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user