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:
71
8_BAR_SETUP.md
Normal file
71
8_BAR_SETUP.md
Normal 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
58
configure_bar.py
Normal 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()
|
34
src/main.py
34
src/main.py
@@ -1,5 +1,4 @@
|
||||
import asyncio
|
||||
import aioespnow
|
||||
|
||||
import patterns
|
||||
from settings import Settings
|
||||
from web import web
|
||||
@@ -28,7 +27,9 @@ def main():
|
||||
sta_if.active(True)
|
||||
|
||||
e = espnow.ESPNow()
|
||||
e.config(rxbuf=1024)
|
||||
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.feed()
|
||||
@@ -50,21 +51,32 @@ def main():
|
||||
data = json.loads(last_msg)
|
||||
defaults = data.get("d", {})
|
||||
bar = data.get(settings.get("name"), {})
|
||||
|
||||
patterns.brightness = bar.get("brightness", defaults.get("brightness", patterns.brightness))
|
||||
patterns.delay = bar.get("delay", defaults.get("delay", patterns.delay))
|
||||
patterns.colors = bar.get("colors", defaults.get("colors", patterns.colors))
|
||||
|
||||
# Check message type
|
||||
message_type = defaults.get("t", "b") # Default to beat if not specified
|
||||
|
||||
# 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.n2 = bar.get("n2", defaults.get("n2", patterns.n2))
|
||||
patterns.n3 = bar.get("n3", defaults.get("n3", patterns.n3))
|
||||
patterns.step = bar.get("pattern_step", defaults.get("step", patterns.step))
|
||||
|
||||
selected_pattern = bar.get("pattern", defaults.get("pattern", "off"))
|
||||
if selected_pattern in patterns.patterns:
|
||||
# Run the selected pattern ONCE in response to this message. Do not auto-tick elsewhere.
|
||||
patterns.patterns[selected_pattern]()
|
||||
# Only execute pattern if it's a beat message
|
||||
if message_type == "b": # Beat message
|
||||
selected_pattern = bar.get("pt", defaults.get("pt", "off"))
|
||||
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:
|
||||
print(f"Pattern {selected_pattern} not found")
|
||||
print(f"Unknown message type: {message_type}")
|
||||
# except Exception as ex:
|
||||
# print(f"Failed to load espnow data {last_msg}: {ex}")
|
||||
# continue
|
||||
|
@@ -15,6 +15,7 @@ class Patterns(PatternBase): # Inherit from PatternBase
|
||||
self.n3 = 1 # Default step factor
|
||||
self.oneshot = False # New: One-shot flag for patterns like fill_range
|
||||
self.patterns = {
|
||||
"off": self.off,
|
||||
"flicker": self.flicker,
|
||||
"fill_range": self.fill_range,
|
||||
"n_chase": self.n_chase,
|
||||
@@ -23,9 +24,25 @@ class Patterns(PatternBase): # Inherit from PatternBase
|
||||
"rainbow": self.rainbow,
|
||||
"specto": self.specto,
|
||||
"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
|
||||
|
||||
def off(self):
|
||||
"""Turn off all LEDs"""
|
||||
self.fill((0, 0, 0))
|
||||
self.n.write()
|
||||
return self.delay
|
||||
|
||||
def flicker(self):
|
||||
current_time = utime.ticks_ms()
|
||||
base_color = self.colors[0]
|
||||
|
@@ -14,9 +14,9 @@ class Settings(dict):
|
||||
|
||||
def set_defaults(self):
|
||||
self["led_pin"] = 4
|
||||
self["num_leds"] = 100
|
||||
self["num_leds"] = 59
|
||||
self["color_order"] = "rgb"
|
||||
self["name"] = f"3"
|
||||
self["name"] = f"103"
|
||||
|
||||
def save(self):
|
||||
try:
|
||||
|
Reference in New Issue
Block a user