Update LED bar to handle message type field

- Process 't' field to distinguish between beat ('b') and update ('u') messages
- Beat messages: execute pattern immediately using current parameters
- Update messages: only update parameters, don't execute pattern
- Maintains backward compatibility with default to beat if 't' not specified
- Enables proper synchronization between controller and bars
This commit is contained in:
2025-09-18 22:10:23 +12:00
parent 748ad4b507
commit 67c4a1a6f6
5 changed files with 171 additions and 13 deletions

71
8_BAR_SETUP.md Normal file
View File

@@ -0,0 +1,71 @@
# 8-LED Bar System Setup
This system supports 8 LED bars working together, each with unique names "100" through "107".
## Quick Setup
### 1. Configure Each LED Bar
Each LED bar needs a unique name. Run the configuration script on each bar:
```bash
python configure_bar.py
```
Then enter the bar name (100, 101, 102, etc.) when prompted.
### 2. Update Bar Names (Optional)
To change the bar names, edit `/home/jimmy/projects/lighting-controller/src/bar_config.py`:
```python
LED_BAR_NAMES = [
"100", # Bar 1
"101", # Bar 2
"102", # Bar 3
"103", # Bar 4
"104", # Bar 5
"105", # Bar 6
"106", # Bar 7
"107", # Bar 8
]
```
### 3. Default Settings
All bars use the same default settings defined in `bar_config.py`:
```python
DEFAULT_BAR_SETTINGS = {
"pattern": "pulse",
"delay": 100,
"colors": [(0, 255, 0)], # Default green
"brightness": 100,
"num_leds": 200,
"n1": 10,
"n2": 10,
"n3": 1,
"n": 0,
}
```
## How It Works
1. **Lighting Controller** sends ESP-NOW messages to all bars simultaneously
2. **Each LED Bar** listens for messages addressed to its unique name
3. **All bars** receive the same pattern/color/brightness settings
4. **Synchronized effects** across all 8 bars
## Current Features
- ✅ All bars show the same pattern simultaneously
- ✅ Individual bar addressing (100-107)
- ✅ Optimized JSON payloads with defaults deduplication
- ✅ Easy configuration via `bar_config.py`
- ✅ MIDI control for all bars
- ✅ n3 step rate functionality
## Future Enhancements
- Sequential patterns (bar 1 → bar 2 → bar 3...)
- Wave effects across bars
- Individual bar control
- Master/slave synchronization
- Physical arrangement awareness

58
configure_bar.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
LED Bar Configuration Script
Updates the settings.json file for each LED bar with its unique name
"""
import json
import os
# LED Bar names/IDs
LED_BAR_NAMES = ["100", "101", "102", "103", "104", "105", "106", "107"]
def update_bar_settings(bar_name, settings_file="settings.json"):
"""Update the settings.json file with the bar name"""
if not os.path.exists(settings_file):
print(f"Error: {settings_file} not found")
return False
# Read current settings
with open(settings_file, 'r') as f:
settings = json.load(f)
# Update the name
settings["name"] = bar_name
# Write back to file
with open(settings_file, 'w') as f:
json.dump(settings, f, indent=4)
print(f"Updated {settings_file} with name: {bar_name}")
return True
def main():
print("LED Bar Configuration Script")
print("=" * 40)
print("Available bar names:", LED_BAR_NAMES)
print()
while True:
print("Enter bar name to configure (or 'quit' to exit):")
bar_name = input("> ").strip()
if bar_name.lower() == 'quit':
break
if bar_name not in LED_BAR_NAMES:
print(f"Invalid bar name. Must be one of: {LED_BAR_NAMES}")
continue
if update_bar_settings(bar_name):
print(f"Successfully configured LED bar as '{bar_name}'")
else:
print("Failed to update settings")
print()
if __name__ == "__main__":
main()

View File

@@ -1,5 +1,4 @@
import asyncio
import aioespnow
import patterns import patterns
from settings import Settings from settings import Settings
from web import web from web import web
@@ -28,7 +27,9 @@ def main():
sta_if.active(True) sta_if.active(True)
e = espnow.ESPNow() e = espnow.ESPNow()
e.config(rxbuf=1024)
e.active(True) e.active(True)
# Increase buffer size for 8-bar payloads (default 526 bytes might be too small) # Set to 1KB to handle larger multi-bar payloads
wdt = machine.WDT(timeout=10000) wdt = machine.WDT(timeout=10000)
wdt.feed() wdt.feed()
@@ -51,20 +52,31 @@ def main():
defaults = data.get("d", {}) defaults = data.get("d", {})
bar = data.get(settings.get("name"), {}) bar = data.get(settings.get("name"), {})
patterns.brightness = bar.get("brightness", defaults.get("brightness", patterns.brightness)) # Check message type
patterns.delay = bar.get("delay", defaults.get("delay", patterns.delay)) message_type = defaults.get("t", "b") # Default to beat if not specified
patterns.colors = bar.get("colors", defaults.get("colors", patterns.colors))
# Always update parameters from message
patterns.brightness = bar.get("br", defaults.get("br", patterns.brightness))
patterns.delay = bar.get("dl", defaults.get("dl", patterns.delay))
patterns.colors = bar.get("cl", defaults.get("cl", patterns.colors))
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.step = bar.get("pattern_step", defaults.get("step", patterns.step)) patterns.step = bar.get("pattern_step", defaults.get("step", patterns.step))
selected_pattern = bar.get("pattern", defaults.get("pattern", "off")) # Only execute pattern if it's a beat message
if selected_pattern in patterns.patterns: if message_type == "b": # Beat message
# Run the selected pattern ONCE in response to this message. Do not auto-tick elsewhere. selected_pattern = bar.get("pt", defaults.get("pt", "off"))
patterns.patterns[selected_pattern]() if selected_pattern in patterns.patterns:
# Run the selected pattern ONCE in response to this beat message
patterns.patterns[selected_pattern]()
else:
print(f"Pattern {selected_pattern} not found")
elif message_type == "u": # Update message
# Just update parameters, don't execute pattern
print(f"Parameters updated: brightness={patterns.brightness}, delay={patterns.delay}")
else: else:
print(f"Pattern {selected_pattern} not found") print(f"Unknown message type: {message_type}")
# except Exception as ex: # except Exception as ex:
# print(f"Failed to load espnow data {last_msg}: {ex}") # print(f"Failed to load espnow data {last_msg}: {ex}")
# continue # continue

View File

@@ -15,6 +15,7 @@ class Patterns(PatternBase): # Inherit from PatternBase
self.n3 = 1 # Default step factor self.n3 = 1 # Default step factor
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 = {
"off": self.off,
"flicker": self.flicker, "flicker": self.flicker,
"fill_range": self.fill_range, "fill_range": self.fill_range,
"n_chase": self.n_chase, "n_chase": self.n_chase,
@@ -23,9 +24,25 @@ class Patterns(PatternBase): # Inherit from PatternBase
"rainbow": self.rainbow, "rainbow": self.rainbow,
"specto": self.specto, "specto": self.specto,
"radiate": self.radiate, "radiate": self.radiate,
# Shortened pattern names for optimized JSON payloads
"o": self.off,
"f": self.flicker,
"fr": self.fill_range,
"nc": self.n_chase,
"a": self.alternating,
"p": self.pulse,
"r": self.rainbow,
"s": self.specto,
"rd": self.radiate,
} }
self.step = 0 self.step = 0
def off(self):
"""Turn off all LEDs"""
self.fill((0, 0, 0))
self.n.write()
return self.delay
def flicker(self): def flicker(self):
current_time = utime.ticks_ms() current_time = utime.ticks_ms()
base_color = self.colors[0] base_color = self.colors[0]

View File

@@ -14,9 +14,9 @@ class Settings(dict):
def set_defaults(self): def set_defaults(self):
self["led_pin"] = 4 self["led_pin"] = 4
self["num_leds"] = 100 self["num_leds"] = 59
self["color_order"] = "rgb" self["color_order"] = "rgb"
self["name"] = f"3" self["name"] = f"103"
def save(self): def save(self):
try: try: