diff --git a/boot.py b/boot.py
new file mode 100644
index 0000000..5b629bb
--- /dev/null
+++ b/boot.py
@@ -0,0 +1,27 @@
+import network
+from machine import Pin
+from config import *
+
+def do_connect():
+ led = Pin(8, Pin.OUT)
+ sta_if = network.WLAN(network.STA_IF)
+ sta_if.ifconfig((ip, '255.255.255.0', gateway, '1.1.1.1'))
+ if not sta_if.isconnected():
+ print('connecting to network...')
+ sta_if.active(True)
+ sta_if.connect(ssid, password)
+ led.on()
+ while not sta_if.isconnected():
+ pass
+ print('network config:', sta_if.ifconfig())
+
+do_connect()
+
+ap = network.WLAN(network.AP_IF)
+ap.active(True)
+ap.config(essid="led", password="qwerty1234")
+print(ap.ifconfig())
+
+
+
+
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..6db7767
--- /dev/null
+++ b/index.html
@@ -0,0 +1,31 @@
+
+
+
+ LED Control
+
+
+
+ Control LEDs
+
+
+
+
+
+
+
+
+
+
diff --git a/main.css b/main.css
new file mode 100644
index 0000000..7b3d403
--- /dev/null
+++ b/main.css
@@ -0,0 +1,40 @@
+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;
+}
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..e601f25
--- /dev/null
+++ b/main.js
@@ -0,0 +1,95 @@
+let delayTimeout;
+
+async function post(path, value) {
+console.log(path, value);
+ try {
+ const response = await fetch(path, {
+ method: "POST",
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ body: value
+ });
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
+ } catch (error) {
+ console.error('Error during POST request:', error);
+ }
+}
+
+async function get(path) {
+ try {
+ const response = await fetch(path);
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
+ return await response.json(); // Assuming you are expecting JSON response
+ } catch (error) {
+ console.error('Error during GET request:', error);
+ }
+}
+
+async function updateColor(event) {
+ event.preventDefault();
+ const color = document.getElementById('color').value;
+ await post("/color", color);
+}
+
+async function updateColor2(event) {
+ event.preventDefault();
+ const color = document.getElementById('color2').value;
+ await post("/color2", color);
+}
+
+async function updatePattern(pattern) {
+ event.preventDefault();
+ await post("/pattern", pattern);
+}
+
+async function updateDelay(event) {
+ event.preventDefault();
+ clearTimeout(delayTimeout);
+ delayTimeout = setTimeout(async function() {
+ const delay = document.getElementById('delay').value;
+ await post('/delay', delay);
+ }, 500);
+}
+
+async function updateNumLeds(event) {
+ event.preventDefault();
+ const numLeds = document.getElementById('num_leds').value;
+ await post('/num_leds', numLeds);
+}
+
+function createPatternButtons(patterns) {
+ const container = document.getElementById('pattern_buttons');
+ container.innerHTML = ''; // Clear previous buttons
+
+ patterns.forEach(pattern => {
+ const button = document.createElement('button');
+ button.type = 'button'; // Use 'button' instead of 'submit'
+ button.textContent = pattern;
+ button.value = pattern;
+ button.addEventListener('click', async function(event) {
+ event.preventDefault();
+ await updatePattern(pattern);
+ });
+ container.appendChild(button);
+ });
+}
+
+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);
+
+ try {
+ const patterns = await get("/patterns");
+ console.log('Patterns fetched:', patterns);
+ createPatternButtons(patterns);
+ } catch (error) {
+ console.error('Error fetching patterns:', error);
+ }
+});
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..9e3b4f4
--- /dev/null
+++ b/main.py
@@ -0,0 +1,187 @@
+from machine import Pin
+from patterns import Patterns
+import socket
+import select
+import json
+
+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
+
+ # Initialize single LED
+ self.led = Pin(led_pin, Pin.OUT)
+
+ # Initialize server
+ self.server_socket = socket.socket()
+ self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.server_socket.bind(('0.0.0.0', 80))
+ self.server_socket.listen(1)
+ self.server_socket.settimeout(1) # Adjust timeout as needed
+
+ 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.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()))
+
+
+
+ def read_file(self, file_path):
+ try:
+ with open(file_path, 'r') as file:
+ return file.read()
+ except OSError as e:
+ 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:
+
+# 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')
+ else:
+ client_socket.send(b'HTTP/1.0 404 Not Found\r\nContent-Type: text/plain\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)
+ 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)
+ elif path == "/main.css":
+ client_socket.send(b'HTTP/1.0 200 OK\r\nContent-type: text/css\r\n\r\n')
+ client_socket.send(self.css)
+ elif path == "/patterns":
+ client_socket.send(b'HTTP/1.0 200 OK\r\nContent-type: application/json\r\n\r\n')
+ client_socket.send(self.patterns_json.encode())
+ else:
+ client_socket.send(b'HTTP/1.0 404 Not Found\r\nContent-Type: text/plain\r\n\r\n')
+
+ def start(self):
+ count = 0
+ try:
+ while True:
+ count += 1
+ events = self.poll.poll(1)
+ for file in events:
+ if file[0] == self.server_socket:
+ client_socket, addr = self.server_socket.accept()
+ request = client_socket.recv(1024).decode()
+ method, path, _ = request.split('\r\n')[0].split()
+ print(f"Method: {method}, Path: {path}")
+ if method == "POST":
+ post_data = request.split('\r\n\r\n')[1] if '\r\n\r\n' in request else ''
+ self.handle_post(path, post_data, client_socket)
+ elif method == "GET":
+ self.handle_get(path, client_socket)
+ client_socket.close()
+
+ if count > 50:
+ self.led.off()
+ if count > 100:
+ self.led.on()
+ count = 0
+
+ self.patterns.tick()
+
+ except Exception as e:
+ print("Error:", e)
+ finally:
+ self.server_socket.close()
+ self.save_settings()
+
+# Example of creating and starting the server
+if __name__ == "__main__":
+ server = LEDServer()
+ server.start()
diff --git a/patterns.py b/patterns.py
index bc2c030..7f7d55a 100644
--- a/patterns.py
+++ b/patterns.py
@@ -1,114 +1,220 @@
from machine import Pin
from neopixel import NeoPixel
-import _thread
-import time, math
-import sys
+import utime
+import random
-class LedPatterns:
- def __init__(self, pin, num_leds):
+class Patterns:
+ def __init__(self, pin, num_leds, color1=(0,0,0), color2=(0,0,0), brightness=127, selected="rainbow_cycle", delay=100):
+ self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
self.num_leds = num_leds
- self.np = NeoPixel(Pin(pin), num_leds)
- self.run = True
+ self.pattern_step = 0
+ self.last_update = utime.ticks_ms()
+ self.delay = delay
+ self.brightness = brightness
+ self.patterns = {
+ "color_wipe": self.color_wipe_step,
+ "rainbow_cycle": self.rainbow_cycle_step,
+ "theater_chase": self.theater_chase_step,
+ "blink": self.blink_step,
+ "random_color_wipe": self.random_color_wipe_step,
+ "random_rainbow_cycle": self.random_rainbow_cycle_step,
+ "random_theater_chase": self.random_theater_chase_step,
+ "random_blink": self.random_blink_step,
+ "color_transition": self.color_transition_step # Added color transition pattern
+ }
+ self.selected = selected
+ self.color1 = color1
+ self.color2 = color2
+ self.transition_duration = 5000 # Duration of color transition in milliseconds
+ self.transition_step = 0
- def color_chase(self, color, wait):
- self.run = True
+ def tick(self):
+ self.patterns[self.selected]()
+
+ def update_num_leds(self, pin, num_leds):
+ self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
+ self.num_leds = num_leds
+ self.pattern_step = 0
+
+ def set_delay(self, delay):
+ self.delay = delay
+
+ def set_brightness(self, brightness):
+ self.brightness = brightness
+
+ def set_color1(self, color):
+ print(color)
+ self.color1 = self.apply_brightness(color)
+
+ def set_color2(self, color):
+ self.color2 = self.apply_brightness(color)
+
+ def apply_brightness(self, color):
+ return tuple(int(c * self.brightness / 255) for c in color)
+
+ def fill(self):
for i in range(self.num_leds):
- self.np[i] = color
- self.np.write()
- if not run:
- break
- time.sleep(wait)
-
- def wheel(self, pos):
- if pos < 0 or pos > 255:
- return (0, 0, 0)
- if pos < 85:
- return (255 - pos * 3, pos * 3, 0)
- if pos < 170:
- pos -= 85
- return (0, 255 - pos * 3, pos * 3)
- pos -= 170
- return (pos * 3, 0, 255 - pos * 3)
-
- def rainbow_cycle(self, wait):
- run = True
- for j in range(255):
+ self.n[i] = self.color1
+ self.n.write()
+
+ def color_wipe_step(self):
+ color = self.apply_brightness(self.color1)
+ current_time = utime.ticks_ms()
+ if utime.ticks_diff(current_time, self.last_update) >= self.delay:
+ 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.write()
+ self.pattern_step += 1
+ else:
+ self.pattern_step = 0
+ self.last_update = current_time
+
+ def rainbow_cycle_step(self):
+ current_time = utime.ticks_ms()
+ if utime.ticks_diff(current_time, self.last_update) >= self.delay:
+ def wheel(pos):
+ if pos < 85:
+ return (pos * 3, 255 - pos * 3, 0)
+ elif pos < 170:
+ pos -= 85
+ return (255 - pos * 3, 0, pos * 3)
+ else:
+ pos -= 170
+ return (0, pos * 3, 255 - pos * 3)
+
for i in range(self.num_leds):
- self.np[i] = self.wheel((i * 256 // self.num_leds) + j)
- self.np.write()
- if not run:
- break
- time.sleep(wait)
-
- def breathing(self, color, duration):
- steps = 256
- for _ in range(steps):
- brightness = int(255 * abs(steps / 2 - _) / (steps / 2))
- self.np.fill((brightness * color[0] // 255, brightness * color[1] // 255, brightness * color[2] // 255))
- self.np.write()
- time.sleep(duration / steps)
-
- def color_transition(self, start_color, end_color, duration):
- steps = 256
- for step in range(steps):
- color = (
- int(start_color[0] + (end_color[0] - start_color[0]) * step / steps),
- int(start_color[1] + (end_color[1] - start_color[1]) * step / steps),
- int(start_color[2] + (end_color[2] - start_color[2]) * step / steps),
- )
- self.np.fill(color)
- self.np.write()
- if not self.running():
- return
- time.sleep(duration / steps)
+ rc_index = (i * 256 // self.num_leds) + self.pattern_step
+ self.n[i] = self.apply_brightness(wheel(rc_index & 255))
+ self.n.write()
+ self.pattern_step = (self.pattern_step + 1) % 256
+ self.last_update = current_time
+
+ def theater_chase_step(self):
+ current_time = utime.ticks_ms()
+ 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
+ else:
+ self.n[i] = (0, 0, 0)
+ self.n.write()
+ self.pattern_step = (self.pattern_step + 1) % 3
+ self.last_update = current_time
+
+ def blink_step(self):
+ current_time = utime.ticks_ms()
+ 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
+ else:
+ for i in range(self.num_leds):
+ self.n[i] = (0, 0, 0)
+ self.n.write()
+ self.pattern_step = (self.pattern_step + 1) % 2
+ self.last_update = current_time
+
+ def random_color_wipe_step(self):
+ current_time = utime.ticks_ms()
+ if utime.ticks_diff(current_time, self.last_update) >= self.delay:
+ color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
+ 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] = self.apply_brightness(color)
+ self.n.write()
+ self.pattern_step += 1
+ else:
+ self.pattern_step = 0
+ self.last_update = current_time
+
+ def random_rainbow_cycle_step(self):
+ current_time = utime.ticks_ms()
+ if utime.ticks_diff(current_time, self.last_update) >= self.delay:
+ def wheel(pos):
+ if pos < 85:
+ return (pos * 3, 255 - pos * 3, 0)
+ elif pos < 170:
+ pos -= 85
+ return (255 - pos * 3, 0, pos * 3)
+ else:
+ pos -= 170
+ return (0, pos * 3, 255 - pos * 3)
+
+ random_offset = random.randint(0, 255)
+ for i in range(self.num_leds):
+ rc_index = (i * 256 // self.num_leds) + self.pattern_step + random_offset
+ self.n[i] = self.apply_brightness(wheel(rc_index & 255))
+ self.n.write()
+ self.pattern_step = (self.pattern_step + 1) % 256
+ self.last_update = current_time
+
+ def random_theater_chase_step(self):
+ current_time = utime.ticks_ms()
+ if utime.ticks_diff(current_time, self.last_update) >= self.delay:
+ color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
+ for i in range(self.num_leds):
+ if (i + self.pattern_step) % 3 == 0:
+ self.n[i] = self.apply_brightness(color)
+ else:
+ self.n[i] = (0, 0, 0)
+ self.n.write()
+ self.pattern_step = (self.pattern_step + 1) % 3
+ self.last_update = current_time
+
+ def random_blink_step(self):
+ current_time = utime.ticks_ms()
+ if utime.ticks_diff(current_time, self.last_update) >= self.delay:
+ color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
+ if self.pattern_step % 2 == 0:
+ for i in range(self.num_leds):
+ self.n[i] = self.apply_brightness(color)
+ else:
+ for i in range(self.num_leds):
+ self.n[i] = (0, 0, 0)
+ self.n.write()
+ self.pattern_step = (self.pattern_step + 1) % 2
+ self.last_update = current_time
+
+ def color_transition_step(self):
+ current_time = utime.ticks_ms()
+ if utime.ticks_diff(current_time, self.last_update) >= self.delay:
+ # Calculate transition factor based on elapsed time
+ transition_factor = (self.pattern_step * 100) / self.transition_duration
+ if transition_factor > 100:
+ transition_factor = 100
+ color = self.interpolate_color(self.color1, self.color2, transition_factor / 100)
- def scanner(self, color, speed):
- position = 0
- direction = 1
-
- while True:
- self.np.fill((0, 0, 0))
- self.np[position] = color
- self.np.write()
- time.sleep(speed)
- position += direction
- if position == self.num_leds - 1 or position == 0:
- direction *= -1
-
- def bidirectional_scanner(self, color_left, color_right=None, speed=0.1):
- color_right = color_right or color_left
- position_left = 0
- position_right = self.num_leds - 1
- direction_left = 1
- direction_right = -1
- while True:
- self.np.fill((0, 0, 0))
- self.np[position_left] = color_left
- self.np[position_right] = color_right
- self.np.write()
- time.sleep(speed)
-
- position_left += direction_left
- position_right += direction_right
-
- if position_left == self.num_leds - 1 or position_left == 0:
- direction_left *= -1
- if position_right == self.num_leds - 1 or position_right == 0:
- direction_right *= -1
-
- def sine_wave_propagation(self, color, speed):
- frequency = 10
- phase = 0
- while True:
- self.np.fill((0, 0, 0))
+ # Apply the interpolated color to all LEDs
for i in range(self.num_leds):
- brightness = int(127.5 * (math.sin(frequency * i + phase) + 1))
- self.np[i] = (brightness * color[0] // 255, brightness * color[1] // 255, brightness * color[2] // 255)
- self.np.write()
- time.sleep(speed)
- phase += 0.1
+ self.n[i] = self.apply_brightness(color)
+ self.n.write()
- def fill(self,color):
- for i in range(self.num_leds):
- self.np[i] = color
- self.np.write()
+ self.pattern_step += self.delay
+ if self.pattern_step > self.transition_duration:
+ self.pattern_step = 0
+
+ self.last_update = current_time
+
+ def interpolate_color(self, color1, color2, factor):
+ return (
+ int(color1[0] + (color2[0] - color1[0]) * factor),
+ int(color1[1] + (color2[1] - color1[1]) * factor),
+ int(color1[2] + (color2[2] - color1[2]) * factor)
+ )
+
+if __name__ == "__main__":
+ p = Patterns(4, 180)
+ p.set_color1((255,0,0))
+ p.set_color2((0,255,0))
+ try:
+ while True:
+ for key in p.patterns:
+ print(key)
+ for _ in range(1000):
+ p.tick()
+ utime.sleep_ms(1)
+ except KeyboardInterrupt:
+ p.fill((0, 0, 0))
diff --git a/scripts.js b/scripts.js
new file mode 100644
index 0000000..87a7e8e
--- /dev/null
+++ b/scripts.js
@@ -0,0 +1,61 @@
+let delayTimeout;
+
+function post(path, value) {
+ fetch(path, {
+ method: "POST",
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ body: encodeURIComponent(value)
+ });
+}
+
+function get(path, value) {
+ fetch(path, {
+ method: "GET",
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ });
+}
+
+function updateColor() {
+ const color = document.getElementById('color').value;
+ post("POST", "/color", color);
+}
+
+function updatePattern(pattern) {
+ const patternButtons = document.querySelectorAll('button[name="pattern"]');
+ //patternButtons.forEach(button => button.disabled = true);
+ post("/pattern", pattern);
+}
+
+function updateDelay() {
+ clearTimeout(delayTimeout);
+ delayTimeout = setTimeout(function() {
+ const delay = document.getElementById('delay').value;
+ post('/delay', delay);
+ }, 500);
+}
+
+function updateNumLeds(event) {
+ event.preventDefault();
+ const numLeds = document.getElementById('num_leds').value;
+ post('/num_leds', numLeds);
+}
+
+document.addEventListener('DOMContentLoaded', function() {
+ document.getElementById('color').addEventListener('input', updateColor);
+ document.getElementById('delay').addEventListener('input', updateDelay);
+ document.getElementById('led_form').addEventListener('submit', updateNumLeds);
+
+ const patternButtons = document.querySelectorAll('button[name="pattern"]');
+ patternButtons.forEach(function(button) {
+ button.addEventListener('click', function(event) {
+ event.preventDefault();
+ const pattern = this.value;
+ updatePattern(pattern);
+ });
+ });
+});
+
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..418247e
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,79 @@
+body {
+ font-family: Arial, sans-serif;
+ padding: 20px;
+}
+
+h1, h2 {
+ color: #333;
+}
+
+form {
+ margin-bottom: 20px;
+}
+
+input[type="number"], input[type="color"], input[type="range"] {
+ margin-right: 10px;
+}
+
+button {
+ padding: 5px 10px;
+ background-color: #007bff;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+button:hover {
+ background-color: #0056b3;
+}
+
+#delay_value {
+ display: inline-block;
+ width: 80px;
+}
+
+#patterns_radio_buttons label {
+ display: block;
+}
+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;
+}
+