Compare commits

...

2 Commits

Author SHA1 Message Date
Jimmy c2b55972d3 Done a heap of stuff 2024-10-06 22:02:39 +13:00
Jimmy c0b0833905 Save settings between powers cycles 2024-10-06 22:01:34 +13:00
6 changed files with 349 additions and 164 deletions

View File

@ -1,31 +1,60 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LED Control</title> <title>LED Control</title>
<script src="main.js" defer></script> <script src="main.js"></script>
<link rel="stylesheet" href="main.css">
</head> </head>
<body> <body>
<h1>Control LEDs</h1> <h1>Control LEDs</h1>
<form id="led_form" method="post" action="/num_leds"> <button onclick="selectControls()">Controls</button>
<label for="num_leds">Number of LEDs:</label> <button onclick="selectSettings()">Settings</button>
<input type="text" id="num_leds" name="num_leds" value="{num_leds}">
<input type="submit" value="Update">
</form>
<div id="pattern_buttons">
<!-- Pattern buttons will be inserted here -->
</div>
<form id="delay_form" method="post" action="/delay">
<label for="delay">Delay:</label>
<input type="range" id="delay" name="delay" min="10" max="1000" value="{delay}" step="10">
</form>
<form id="color_form" method="post" action="/color">
<label for="color">Select Color:</label>
<input type="color" id="color" name="color" value="{color}">
</form>
<form id="color2_form" method="post" action="/color2">
<label for="color2">Select Color2:</label>
<input type="color" id="color2" name="color2" value="{color2}">
</form>
<!-- Main LED Controls -->
<div id="controls">
<div id="pattern_buttons">
<!-- Pattern buttons will be inserted here -->
</div>
<form id="delay_form" method="post" action="/delay">
<label for="delay">Delay:</label>
<input type="range" id="delay" name="delay" min="1" max="1000" value="{delay}" step="10">
</form>
<form id="brightness_form" method="post" action="/brightness">
<label for="brightness">Brightness:</label>
<input type="range" id="brightness" name="brightness" min="0" max="100" value="{brightness}" step="1">
</form>
<form id="color_form" method="post" action="/color">
<input type="color" id="color" name="color" value="{color}">
</form>
<form id="color2_form" method="post" action="/color2">
<input type="color" id="color2" name="color2" value="{color2}">
</form>
</div>
<!-- Settings Menu for num_leds, Wi-Fi SSID, and Password -->
<div id="settings_menu" style="display: none;">
<h2>Settings</h2>
<!-- Separate form for submitting num_leds -->
<form id="num_leds_form" method="post" action="/num_leds">
<label for="num_leds">Number of LEDs:</label>
<input type="text" id="num_leds" name="num_leds" value="{num_leds}">
<input type="submit" value="Update Number of LEDs">
</form>
<!-- Form for Wi-Fi SSID and password -->
<form id="wifi_form" method="post" action="/wifi_settings">
<label for="ssid">Wi-Fi SSID:</label>
<input type="text" id="ssid" name="ssid" value="{ssid}">
<br>
<label for="password">Wi-Fi Password:</label>
<input type="password" id="password" name="password">
<br>
<input type="submit" value="Save Wi-Fi Settings">
</form>
</div>
</body> </body>
</html> </html>

112
main.css
View File

@ -1,40 +1,74 @@
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
background-color: #f0f0f0; max-width: 600px;
margin: 0; margin: 0 auto;
padding: 20px; padding: 20px;
} line-height: 1.6;
}
h1 { h1 {
color: #333; text-align: center;
} }
form {
form { margin-bottom: 20px;
margin-bottom: 20px; }
} label {
display: block;
label { margin-bottom: 5px;
display: block; }
margin-bottom: 5px; input[type="text"], input[type="submit"], input[type="range"], input[type="color"] {
font-weight: bold; width: 100%;
}
margin-bottom: 10px;
input[type="text"], box-sizing: border-box;
input[type="range"], }
input[type="color"], input[type="range"] {
button { -webkit-appearance: none;
margin-bottom: 10px; appearance: none;
padding: 10px; height: 25px;
font-size: 16px; background: #d3d3d3;
} outline: none;
opacity: 0.7;
button:disabled { transition: opacity .2s;
background-color: #ccc; }
} input[type="range"]:hover {
opacity: 1;
button { }
margin-right: 5px; input[type="range"]::-webkit-slider-thumb {
padding: 10px 20px; -webkit-appearance: none;
font-size: 16px; appearance: none;
cursor: pointer; 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);
}
}

58
main.js
View File

@ -1,4 +1,7 @@
let delayTimeout; let delayTimeout;
let brightnessTimeout;
let colorTimeout;
let color2Timeout;
async function post(path, value) { async function post(path, value) {
console.log(path, value); console.log(path, value);
@ -32,14 +35,20 @@ async function get(path) {
async function updateColor(event) { async function updateColor(event) {
event.preventDefault(); event.preventDefault();
const color = document.getElementById('color').value; clearTimeout(colorTimeout);
await post("/color", color); colorTimeout = setTimeout(async function() {
const color = document.getElementById('color').value;
await post("/color", color);
}, 500);
} }
async function updateColor2(event) { async function updateColor2(event) {
event.preventDefault(); event.preventDefault();
const color = document.getElementById('color2').value; clearTimeout(color2Timeout);
await post("/color2", color); color2Timeout = setTimeout(async function() {
const color = document.getElementById('color2').value;
await post("/color2", color);
}, 500);
} }
async function updatePattern(pattern) { async function updatePattern(pattern) {
@ -47,6 +56,15 @@ async function updatePattern(pattern) {
await post("/pattern", 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) { async function updateDelay(event) {
event.preventDefault(); event.preventDefault();
clearTimeout(delayTimeout); clearTimeout(delayTimeout);
@ -62,6 +80,14 @@ async function updateNumLeds(event) {
await post('/num_leds', numLeds); 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) { function createPatternButtons(patterns) {
const container = document.getElementById('pattern_buttons'); const container = document.getElementById('pattern_buttons');
container.innerHTML = ''; // Clear previous buttons container.innerHTML = ''; // Clear previous buttons
@ -83,7 +109,11 @@ document.addEventListener('DOMContentLoaded', async function() {
document.getElementById('color').addEventListener('input', updateColor); document.getElementById('color').addEventListener('input', updateColor);
document.getElementById('color2').addEventListener('input', updateColor2); document.getElementById('color2').addEventListener('input', updateColor2);
document.getElementById('delay').addEventListener('input', updateDelay); 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 { try {
const patterns = await get("/patterns"); const patterns = await get("/patterns");
@ -92,4 +122,22 @@ document.addEventListener('DOMContentLoaded', async function() {
} catch (error) { } catch (error) {
console.error('Error fetching patterns:', 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';
}

182
main.py
View File

@ -1,21 +1,24 @@
from machine import Pin from machine import Pin
from patterns import Patterns from patterns import Patterns
from settings import Settings
import socket import socket
import select import select
import json import json
import utime
import sys
class LEDServer: class LEDServer:
SETTINGS_FILE = "/settings.json" # Path should be adjusted for MicroPython's filesystem 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): def __init__(self, num_leds=50, pin=4, led_pin=8, brigtness=255):
# Initialize NeoPixel Patterns # Initialize NeoPixel Patterns
self.num_leds = num_leds self.settings = Settings()
self.patterns = Patterns(pin, num_leds)
self.selected_pattern = "blink"
self.color = (16, 16, 0)
self.color2 = (16, 16, 0)
self.delay = 100
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 # Initialize single LED
self.led = Pin(led_pin, Pin.OUT) self.led = Pin(led_pin, Pin.OUT)
@ -29,13 +32,7 @@ class LEDServer:
self.poll = select.poll() self.poll = select.poll()
self.poll.register(self.server_socket, select.POLLIN) self.poll.register(self.server_socket, select.POLLIN)
# Load settings from file self.html = self.read_file("/index.html")
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.js = self.read_file("/main.js").encode('utf-8')
self.css = self.read_file("/main.css").encode('utf-8') self.css = self.read_file("/main.css").encode('utf-8')
self.patterns_json = json.dumps(list(self.patterns.patterns.keys())) self.patterns_json = json.dumps(list(self.patterns.patterns.keys()))
@ -50,92 +47,88 @@ class LEDServer:
print(f"Error reading file {file_path}: {e}") print(f"Error reading file {file_path}: {e}")
return "" 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): def handle_post(self, path, post_data, client_socket):
print(post_data) paths = {
if path == "/num_leds": "/num_leds" : self.num_leds,
try: "/pattern": self.pattern,
self.num_leds = int(post_data) "/delay": self.delay,
self.patterns.update_num_leds(4, self.num_leds) "/brightness": self.brightness,
self.save_settings() "/color": self.color,
client_socket.send(b'HTTP/1.0 200 OK\r\n\r\n') "/color2": self.color2
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') if path in paths:
elif path == "/color2": paths[path](client_socket, post_data)
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: else:
client_socket.send(b'HTTP/1.0 404 Not Found\r\nContent-Type: text/plain\r\n\r\n') 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): def handle_get(self, path, client_socket):
if path == "/": if path == "/":
client_socket.send(b'HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') 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": 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(b'HTTP/1.0 200 OK\r\nContent-type: application/javascript\r\n\r\n')
client_socket.send(self.js) client_socket.send(self.js)
@ -150,6 +143,8 @@ class LEDServer:
def start(self): def start(self):
count = 0 count = 0
average_time = 0
previous_time = utime.ticks_us()
try: try:
while True: while True:
count += 1 count += 1
@ -169,17 +164,18 @@ class LEDServer:
if count > 50: if count > 50:
self.led.off() self.led.off()
#print(utime.ticks_us() - previous_time)
if count > 100: if count > 100:
self.led.on() self.led.on()
count = 0 count = 0
# previous_time = utime.ticks_us()
self.patterns.tick() self.patterns.tick()
except Exception as e: except Exception as e:
print("Error:", e) print("Error:", e)
sys.print_exception(e)
finally: finally:
self.server_socket.close() self.server_socket.close()
self.save_settings()
# Example of creating and starting the server # Example of creating and starting the server
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -12,6 +12,8 @@ class Patterns:
self.delay = delay self.delay = delay
self.brightness = brightness self.brightness = brightness
self.patterns = { self.patterns = {
"off": self.off,
"on" : self.on,
"color_wipe": self.color_wipe_step, "color_wipe": self.color_wipe_step,
"rainbow_cycle": self.rainbow_cycle_step, "rainbow_cycle": self.rainbow_cycle_step,
"theater_chase": self.theater_chase_step, "theater_chase": self.theater_chase_step,
@ -25,7 +27,7 @@ class Patterns:
self.selected = selected self.selected = selected
self.color1 = color1 self.color1 = color1
self.color2 = color2 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 self.transition_step = 0
def tick(self): def tick(self):
@ -52,11 +54,30 @@ class Patterns:
def apply_brightness(self, color): def apply_brightness(self, color):
return tuple(int(c * self.brightness / 255) for c in 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): def fill(self):
for i in range(self.num_leds): for i in range(self.num_leds):
self.n[i] = self.color1 self.n[i] = self.color1
self.n.write() 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): def color_wipe_step(self):
color = self.apply_brightness(self.color1) color = self.apply_brightness(self.color1)
current_time = utime.ticks_ms() current_time = utime.ticks_ms()
@ -64,7 +85,7 @@ class Patterns:
if self.pattern_step < self.num_leds: if self.pattern_step < self.num_leds:
for i in range(self.num_leds): for i in range(self.num_leds):
self.n[i] = (0, 0, 0) 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.n.write()
self.pattern_step += 1 self.pattern_step += 1
else: else:
@ -96,7 +117,7 @@ class Patterns:
if utime.ticks_diff(current_time, self.last_update) >= self.delay: if utime.ticks_diff(current_time, self.last_update) >= self.delay:
for i in range(self.num_leds): for i in range(self.num_leds):
if (i + self.pattern_step) % 3 == 0: if (i + self.pattern_step) % 3 == 0:
self.n[i] = self.color1 self.n[i] = self.apply_brightness(self.color1)
else: else:
self.n[i] = (0, 0, 0) self.n[i] = (0, 0, 0)
self.n.write() self.n.write()
@ -108,7 +129,7 @@ class Patterns:
if utime.ticks_diff(current_time, self.last_update) >= self.delay: if utime.ticks_diff(current_time, self.last_update) >= self.delay:
if self.pattern_step % 2 == 0: if self.pattern_step % 2 == 0:
for i in range(self.num_leds): for i in range(self.num_leds):
self.n[i] = self.color1 self.n[i] = self.apply_brightness(self.color1)
else: else:
for i in range(self.num_leds): for i in range(self.num_leds):
self.n[i] = (0, 0, 0) self.n[i] = (0, 0, 0)
@ -209,11 +230,13 @@ if __name__ == "__main__":
p = Patterns(4, 180) p = Patterns(4, 180)
p.set_color1((255,0,0)) p.set_color1((255,0,0))
p.set_color2((0,255,0)) p.set_color2((0,255,0))
#p.set_delay(10)
try: try:
while True: while True:
for key in p.patterns: for key in p.patterns:
print(key) print(key)
for _ in range(1000): p.select(key)
for _ in range(2000):
p.tick() p.tick()
utime.sleep_ms(1) utime.sleep_ms(1)
except KeyboardInterrupt: except KeyboardInterrupt:

55
settings.py Normal file
View File

@ -0,0 +1,55 @@
import json
import os
import uasyncio as asyncio
class Settings(dict):
SETTINGS_FILE = "/settings.json"
def __init__(self):
super().__init__()
self.load() # Load settings from file during initialization
def set_defaults(self):
self["num_leds"] = 50
self["selected_pattern"] = "blink"
self["color1"] = "#000f00"
self["color2"] = "#0f0000"
self["delay"] = 100
self["brightness"] = 100
self["wifi"] = {"ssid": "", "password": ""}
def save(self):
try:
j = json.dumps(self)
with open(self.SETTINGS_FILE, 'w') as file:
file.write(j)
print("Settings saved successfully.")
except Exception as e:
print(f"Error saving settings: {e}")
def load(self):
try:
with open(self.SETTINGS_FILE, 'r') as file:
loaded_settings = json.load(file)
self.update(loaded_settings)
print("Settings loaded successfully.")
except Exception as e:
print(f"Error loading settings: {e}")
self.set_defaults()
# Example usage
def main():
settings = Settings()
print(f"Number of LEDs: {settings['num_leds']}")
settings['num_leds'] = 100
print(f"Updated number of LEDs: {settings['num_leds']}")
settings.save()
# Create a new Settings object to test loading
new_settings = Settings()
print(f"Loaded number of LEDs: {new_settings['num_leds']}")
print(settings)
# Run the example
if __name__ == "__main__":
main()