diff --git a/index.html b/index.html index 6db7767..b557c99 100644 --- a/index.html +++ b/index.html @@ -1,31 +1,60 @@ - + + + LED Control - + +

Control LEDs

-
- - - -
-
- -
-
- - -
-
- - -
-
- - -
+ + + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ + + + diff --git a/main.css b/main.css index 7b3d403..0c393e7 100644 --- a/main.css +++ b/main.css @@ -1,40 +1,74 @@ body { - font-family: Arial, sans-serif; - background-color: #f0f0f0; - margin: 0; - padding: 20px; -} - -h1 { - color: #333; -} - -form { - margin-bottom: 20px; -} - -label { - display: block; - margin-bottom: 5px; - font-weight: bold; -} - -input[type="text"], -input[type="range"], -input[type="color"], -button { - margin-bottom: 10px; - padding: 10px; - font-size: 16px; -} - -button:disabled { - background-color: #ccc; -} - -button { - margin-right: 5px; - padding: 10px 20px; - font-size: 16px; - cursor: pointer; -} + font-family: Arial, sans-serif; + max-width: 600px; + margin: 0 auto; + padding: 20px; + line-height: 1.6; + } + h1 { + text-align: center; + } + form { + margin-bottom: 20px; + } + label { + display: block; + margin-bottom: 5px; + } + input[type="text"], input[type="submit"], input[type="range"], input[type="color"] { + width: 100%; + + margin-bottom: 10px; + box-sizing: border-box; + } + input[type="range"] { + -webkit-appearance: none; + appearance: none; + height: 25px; + background: #d3d3d3; + outline: none; + opacity: 0.7; + transition: opacity .2s; + } + input[type="range"]:hover { + opacity: 1; + } + input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 25px; + height: 25px; + background: #4CAF50; + cursor: pointer; + border-radius: 50%; + } + input[type="range"]::-moz-range-thumb { + width: 25px; + height: 25px; + background: #4CAF50; + cursor: pointer; + border-radius: 50%; + } + #pattern_buttons { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-bottom: 20px; + } + #pattern_buttons button { + flex: 1 0 calc(33.333% - 10px); + padding: 10px; + background-color: #4CAF50; + color: white; + border: none; + cursor: pointer; + transition: background-color 0.3s; + } + #pattern_buttons button:hover { + background-color: #45a049; + } + @media (max-width: 480px) { + #pattern_buttons button { + flex: 1 0 calc(50% - 10px); + } + } \ No newline at end of file diff --git a/main.js b/main.js index e601f25..b33fcd7 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,7 @@ let delayTimeout; +let brightnessTimeout; +let colorTimeout; +let color2Timeout; async function post(path, value) { console.log(path, value); @@ -32,14 +35,20 @@ async function get(path) { async function updateColor(event) { event.preventDefault(); - const color = document.getElementById('color').value; - await post("/color", color); + clearTimeout(colorTimeout); + colorTimeout = setTimeout(async function() { + const color = document.getElementById('color').value; + await post("/color", color); + }, 500); } async function updateColor2(event) { event.preventDefault(); - const color = document.getElementById('color2').value; - await post("/color2", color); + clearTimeout(color2Timeout); + color2Timeout = setTimeout(async function() { + const color = document.getElementById('color2').value; + await post("/color2", color); + }, 500); } async function updatePattern(pattern) { @@ -47,6 +56,15 @@ async function updatePattern(pattern) { await post("/pattern", pattern); } +async function updateBrightness(event) { + event.preventDefault(); + clearTimeout(brightnessTimeout); + brightnessTimeout = setTimeout(async function() { + const brightness = document.getElementById('brightness').value; + await post('/brightness', brightness); + }, 500); +} + async function updateDelay(event) { event.preventDefault(); clearTimeout(delayTimeout); @@ -62,6 +80,14 @@ async function updateNumLeds(event) { await post('/num_leds', numLeds); } +async function updateWifi(event) { + event.preventDefault(); + const ssid = document.getElementById('ssid').value; + const password = document.getElementById('password').value; + console.log(ssid, password); + //await post('/num_leds', numLeds); +} + function createPatternButtons(patterns) { const container = document.getElementById('pattern_buttons'); container.innerHTML = ''; // Clear previous buttons @@ -83,7 +109,11 @@ document.addEventListener('DOMContentLoaded', async function() { document.getElementById('color').addEventListener('input', updateColor); document.getElementById('color2').addEventListener('input', updateColor2); document.getElementById('delay').addEventListener('input', updateDelay); - document.getElementById('led_form').addEventListener('submit', updateNumLeds); + document.getElementById('brightness').addEventListener('input', updateBrightness); + document.getElementById('num_leds_form').addEventListener('submit', updateNumLeds); + document.getElementById('wifi_form').addEventListener('submit', updateWifi); + document.getElementById('delay').addEventListener('touchend', updateDelay); + document.getElementById('brightness').addEventListener('touchend', updateBrightness); try { const patterns = await get("/patterns"); @@ -92,4 +122,22 @@ document.addEventListener('DOMContentLoaded', async function() { } catch (error) { console.error('Error fetching patterns:', error); } + + }); + + +// Function to toggle the display of the settings menu +function selectSettings() { + const settingsMenu = document.getElementById('settings_menu'); + controls = document.getElementById('controls'); + settingsMenu.style.display = 'block'; + controls.style.display = 'none'; +} + +function selectControls() { + const settingsMenu = document.getElementById('settings_menu'); + controls = document.getElementById('controls'); + settingsMenu.style.display = 'none'; + controls.style.display = 'block'; +} diff --git a/main.py b/main.py index 9e3b4f4..c5729b8 100644 --- a/main.py +++ b/main.py @@ -1,21 +1,24 @@ from machine import Pin from patterns import Patterns +from settings import Settings import socket import select import json - +import utime +import sys + class LEDServer: SETTINGS_FILE = "/settings.json" # Path should be adjusted for MicroPython's filesystem def __init__(self, num_leds=50, pin=4, led_pin=8, brigtness=255): # Initialize NeoPixel Patterns - self.num_leds = num_leds - self.patterns = Patterns(pin, num_leds) - self.selected_pattern = "blink" - self.color = (16, 16, 0) - self.color2 = (16, 16, 0) - self.delay = 100 + self.settings = Settings() + print(self.settings) + self.patterns = Patterns(pin, num_leds) + self.patterns.select(self.settings["selected_pattern"]) + self.patterns.set_color1(tuple(int(self.settings["color1"][i:i+2], 16) for i in (1, 5, 3))) + self.patterns.set_color2(tuple(int(self.settings["color2"][i:i+2], 16) for i in (1, 5, 3))) # Initialize single LED self.led = Pin(led_pin, Pin.OUT) @@ -29,13 +32,7 @@ class LEDServer: self.poll = select.poll() self.poll.register(self.server_socket, select.POLLIN) - # Load settings from file - self.load_settings() - # Read static files - color = f'#{self.color[0]:02x}{self.color[1]:02x}{self.color[2]:02x}' - color2 = f'#{self.color2[0]:02x}{self.color2[1]:02x}{self.color2[2]:02x}' - print(color) - self.html = self.read_file("/index.html").format(num_leds=self.num_leds, delay=self.delay, color=color, color2=color2).encode() + self.html = self.read_file("/index.html") self.js = self.read_file("/main.js").encode('utf-8') self.css = self.read_file("/main.css").encode('utf-8') self.patterns_json = json.dumps(list(self.patterns.patterns.keys())) @@ -50,92 +47,88 @@ class LEDServer: print(f"Error reading file {file_path}: {e}") return "" - def save_settings(self): - settings = { - "num_leds": self.num_leds, - "selected_pattern": self.selected_pattern, - "color": self.color, - "color2": self.color2, - "delay": self.delay - } - print(settings) - try: - with open(self.SETTINGS_FILE, 'w') as file: - json.dump(settings, file) - except OSError as e: - print(f"Error saving settings: {e}") - - def load_settings(self): - if self.file_exists(self.SETTINGS_FILE): - try: - with open(self.SETTINGS_FILE, 'r') as file: - settings = json.load(file) - self.num_leds = settings.get("num_leds", self.num_leds) - self.selected_pattern = settings.get("selected_pattern", self.selected_pattern) - self.color = tuple(settings.get("color", self.color)) - self.color2 = tuple(settings.get("color2", self.color2)) - self.delay = settings.get("delay", self.delay) - self.patterns.update_num_leds(4, self.num_leds) - self.patterns.set_delay(self.delay) - self.patterns.selected = self.selected_pattern - except (OSError, ValueError) as e: - print(f"Error loading settings: {e}") - - def file_exists(self, file_path): - try: - with open(file_path, 'r'): - return True - except OSError: - return False def handle_post(self, path, post_data, client_socket): - print(post_data) - if path == "/num_leds": - try: - self.num_leds = int(post_data) - self.patterns.update_num_leds(4, self.num_leds) - self.save_settings() - client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') - except ValueError: - client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') - - elif path == "/pattern": - self.selected_pattern = post_data - self.patterns.selected = post_data - self.save_settings() - client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') - - elif path == "/delay": - try: - self.delay = int(post_data) - self.patterns.set_delay(self.delay) - self.save_settings() - client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') - except ValueError: - client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') - - elif path == "/color": -# try: - self.patterns.set_color1(tuple(int(post_data[i:i+2], 16) for i in (1, 3, 5))) # Convert hex to RGB - self.save_settings() - client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') -# except: + paths = { + "/num_leds" : self.num_leds, + "/pattern": self.pattern, + "/delay": self.delay, + "/brightness": self.brightness, + "/color": self.color, + "/color2": self.color2 + } -# client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') - elif path == "/color2": - try: - self.patterns.set_color2(tuple(int(post_data[i:i+2], 16) for i in (1, 3, 5))) # Convert hex to RGB - self.save_settings() - client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') - except: - client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') + if path in paths: + paths[path](client_socket, post_data) else: client_socket.send(b'HTTP/1.0 404 Not Found\r\nContent-Type: text/plain\r\n\r\n') + def num_leds(self, client_socket, post_data): + try: + num_leds = int(post_data) + self.patterns.update_num_leds(4,num_leds) + self.settings["num_leds"] = num_leds + self.settings.save() + client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') + except ValueError: + client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') + + def pattern(self, client_socket, post_data): + if self.patterns.select(post_data): + self.settings["selected_pattern"] = post_data + self.settings.save() + client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') + else: + client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') + + def delay(self, client_socket, post_data): + try: + delay = int(post_data) + self.patterns.set_delay(delay) + self.settings["delay"] = delay + self.settings.save() + client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') + except ValueError: + client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') + + def brightness(self, client_socket, post_data): + try: + brightness = int(post_data) + self.patterns.set_brightness(brightness) + self.settings["brightness"] = brightness + self.settings.save() + client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') + except ValueError: + client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') + + def color(self, client_socket, post_data): + try: + self.patterns.set_color1(tuple(int(post_data[i:i+2], 16) for i in (1, 5, 3))) # Convert hex to RGB + self.settings["color1"] = post_data + self.settings.save() + client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') + except: + client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') + + def color2(self, client_socket, post_data): + try: + self.patterns.set_color2(tuple(int(post_data[i:i+2], 16) for i in (1, 5, 3))) # Convert hex to RGB + self.settings["color2"] = post_data + self.settings.save() + client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') + except: + client_socket.send(b'HTTP/1.0 400 Bad request\r\n\r\n') + def handle_get(self, path, client_socket): if path == "/": client_socket.send(b'HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') - client_socket.send(self.html) + client_socket.send(self.html.format( + num_leds=self.settings["num_leds"], + delay=self.settings["delay"], + brightness=self.settings["brightness"], + color=self.settings["color1"], + color2=self.settings["color2"], + ssid=self.settings["wifi"]["ssid"]).encode()) elif path == "/main.js": client_socket.send(b'HTTP/1.0 200 OK\r\nContent-type: application/javascript\r\n\r\n') client_socket.send(self.js) @@ -150,6 +143,8 @@ class LEDServer: def start(self): count = 0 + average_time = 0 + previous_time = utime.ticks_us() try: while True: count += 1 @@ -169,17 +164,18 @@ class LEDServer: if count > 50: self.led.off() + #print(utime.ticks_us() - previous_time) if count > 100: self.led.on() count = 0 - + # previous_time = utime.ticks_us() self.patterns.tick() except Exception as e: print("Error:", e) + sys.print_exception(e) finally: self.server_socket.close() - self.save_settings() # Example of creating and starting the server if __name__ == "__main__": diff --git a/patterns.py b/patterns.py index 7f7d55a..e399298 100644 --- a/patterns.py +++ b/patterns.py @@ -12,6 +12,8 @@ class Patterns: self.delay = delay self.brightness = brightness self.patterns = { + "off": self.off, + "on" : self.on, "color_wipe": self.color_wipe_step, "rainbow_cycle": self.rainbow_cycle_step, "theater_chase": self.theater_chase_step, @@ -25,7 +27,7 @@ class Patterns: self.selected = selected self.color1 = color1 self.color2 = color2 - self.transition_duration = 5000 # Duration of color transition in milliseconds + self.transition_duration = 50 # Duration of color transition in milliseconds self.transition_step = 0 def tick(self): @@ -52,11 +54,30 @@ class Patterns: def apply_brightness(self, color): return tuple(int(c * self.brightness / 255) for c in color) + def select(self, pattern): + if pattern in self.patterns: + self.selected = pattern + return True + return False + def fill(self): for i in range(self.num_leds): self.n[i] = self.color1 self.n.write() + def off(self): + color = self.color1 + self.color1 = (0,0,0) + self.fill() + self.color1 = color + + def on(self): + color = self.color1 + self.color1 = self.apply_brightness(self.color1) + self.fill() + self.color1 = color + + def color_wipe_step(self): color = self.apply_brightness(self.color1) current_time = utime.ticks_ms() @@ -64,7 +85,7 @@ class Patterns: if self.pattern_step < self.num_leds: for i in range(self.num_leds): self.n[i] = (0, 0, 0) - self.n[self.pattern_step] = color + self.n[self.pattern_step] = self.apply_brightness(color) self.n.write() self.pattern_step += 1 else: @@ -96,7 +117,7 @@ class Patterns: if utime.ticks_diff(current_time, self.last_update) >= self.delay: for i in range(self.num_leds): if (i + self.pattern_step) % 3 == 0: - self.n[i] = self.color1 + self.n[i] = self.apply_brightness(self.color1) else: self.n[i] = (0, 0, 0) self.n.write() @@ -108,7 +129,7 @@ class Patterns: if utime.ticks_diff(current_time, self.last_update) >= self.delay: if self.pattern_step % 2 == 0: for i in range(self.num_leds): - self.n[i] = self.color1 + self.n[i] = self.apply_brightness(self.color1) else: for i in range(self.num_leds): self.n[i] = (0, 0, 0) @@ -209,11 +230,13 @@ if __name__ == "__main__": p = Patterns(4, 180) p.set_color1((255,0,0)) p.set_color2((0,255,0)) + #p.set_delay(10) try: while True: for key in p.patterns: print(key) - for _ in range(1000): + p.select(key) + for _ in range(2000): p.tick() utime.sleep_ms(1) except KeyboardInterrupt: