From 9a58d5f11899545e2425ca3f3b16c0a51c2c6068 Mon Sep 17 00:00:00 2001 From: Lucca Ketterer Date: Sat, 14 Jan 2023 01:01:51 +0100 Subject: [PATCH] project reimplemented --- .gitignore | 3 - README.md | 17 ---- client/Makefile | 3 - client/config.toml | 0 client/lc.toml | 8 -- client/requirements.txt | 1 - controller/Makefile | 3 + {client => controller}/cava.conf | 1 + client/client.py => controller/lc | 148 ++++++++++++++++++++++-------- pi/Makefile | 6 ++ pi/copy_to_pi.sh | 5 + pi/lc.py | 52 +++++++++++ pi/lc.service | 19 ++++ server/color_mode.py | 34 ------- server/copy_to_pi.sh | 2 - server/example/demo.py | 101 -------------------- server/example/rainbow.py | 46 ---------- server/led_controll.py | 47 ---------- server/requirements.txt | 5 - server/server.py | 33 ------- server/ws2801.py | 25 ----- 21 files changed, 196 insertions(+), 363 deletions(-) delete mode 100644 .gitignore delete mode 100644 README.md delete mode 100644 client/Makefile delete mode 100644 client/config.toml delete mode 100644 client/lc.toml delete mode 100644 client/requirements.txt create mode 100644 controller/Makefile rename {client => controller}/cava.conf (99%) rename client/client.py => controller/lc (63%) create mode 100644 pi/Makefile create mode 100755 pi/copy_to_pi.sh create mode 100755 pi/lc.py create mode 100644 pi/lc.service delete mode 100644 server/color_mode.py delete mode 100755 server/copy_to_pi.sh delete mode 100644 server/example/demo.py delete mode 100644 server/example/rainbow.py delete mode 100755 server/led_controll.py delete mode 100644 server/requirements.txt delete mode 100644 server/server.py delete mode 100644 server/ws2801.py diff --git a/.gitignore b/.gitignore deleted file mode 100644 index baf1b00..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.ipynb* -*/__pycache__/ - diff --git a/README.md b/README.md deleted file mode 100644 index 111e349..0000000 --- a/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# led controll - -## client - - -## server - -### requirements - -SPI has to be enabled: -`sudo raspi-config -> Interface -> SPI` - -### usage - --d : (deamon mode) receive tcp packages with rgb color code in hex format on port 5000 --s $(hex) : (direct mode) used to set color directly --h : help menu diff --git a/client/Makefile b/client/Makefile deleted file mode 100644 index 9fccd30..0000000 --- a/client/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -install : - cp client.py /bin/lc; mkdir -p /etc/lc; cp cava.conf /etc/lc/cava.conf; sudo chmod 666 /etc/lc/cava.conf - diff --git a/client/config.toml b/client/config.toml deleted file mode 100644 index e69de29..0000000 diff --git a/client/lc.toml b/client/lc.toml deleted file mode 100644 index 76c5dbd..0000000 --- a/client/lc.toml +++ /dev/null @@ -1,8 +0,0 @@ -[server] -ip = "172.0.0.1" -port = "5001" - -[server] -ip = "172.0.0.1" -port = "5000" - diff --git a/client/requirements.txt b/client/requirements.txt deleted file mode 100644 index 24ce15a..0000000 --- a/client/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -numpy diff --git a/controller/Makefile b/controller/Makefile new file mode 100644 index 0000000..2547d27 --- /dev/null +++ b/controller/Makefile @@ -0,0 +1,3 @@ +install : + cp lc /bin/lc; mkdir -p ~/.config/lc; cp cava.conf ~/.config/lc/cava.conf; sudo chmod 666 /etc/lc/cava.conf + diff --git a/client/cava.conf b/controller/cava.conf similarity index 99% rename from client/cava.conf rename to controller/cava.conf index 89ae9f8..3b49584 100644 --- a/client/cava.conf +++ b/controller/cava.conf @@ -21,6 +21,7 @@ sensitivity = 1000 # The number of bars (0-200). 0 sets it to auto (fill up console). # Bars' width and space between bars in number of characters. +;bars = 128 bars = 2 ; bar_width = 2 ; bar_spacing = 1 diff --git a/client/client.py b/controller/lc similarity index 63% rename from client/client.py rename to controller/lc index afcaa90..b258111 100755 --- a/client/client.py +++ b/controller/lc @@ -1,48 +1,119 @@ #!/usr/bin/python3 import socket -import getopt -import sys +import os import subprocess -import pyscreenshot -#from PIL import Image, ImageDraw, ImageFont -#import cv2 -import numpy as np -#from skimage import io from threading import Thread -import time -import io -import time -#from gi.repository import GLib -#from pydbus import SessionBus import curses -import toml +import asyncio +import click +import pickle +import re -s1 = socket.socket() -s2 = socket.socket() -server = ["192.168.188.64", "192.168.188.76"] +async def scan_for_pi() -> dict(): + ip = socket.gethostbyname(socket.gethostname()) + baseIP = '.'.join(ip.split(".")[:3]) -def connect(port = 5000): - s1.connect((server[0], port)) - s2.connect((server[1], port)) -def disconnect(): - s1.close() # close the connection - s2.close() -def send(data): - s1.send(data.encode()) # send message - s2.send(data.encode()) # send message + def scan(ip: str) -> str: + try: + with socket.socket() as s: + s.settimeout(0.5) + s.connect((ip, 5000)) + s.send("PING".encode()) + data = s.recv(1024).decode() + if data == "PONG": + print("found:", ip) + return ip + except OSError: + pass -def helpmenu(): - print("light controll\n") - print("Options:") - print("-h show help") - print("-s set static color") - print("-v visualizer") - print("-i interactive interface") - print("-a ambient light") - print("-t test function (debug)") + async def scan_async(ip: str) -> str: + return await asyncio.to_thread(scan, ip) + + pi_list = await asyncio.gather(*[scan_async(baseIP + "." + str(i)) for i in range(255)]) + return [pi for pi in pi_list if pi is not None] -def base_color(color): - return [i // min(color) for i in color] +class PI: + def __init__(self, ip, port=5000): + self.ip = ip + self.port = port + self.connected = False + self.socket = socket.socket() + + def __str__(self): + return self.ip + + def connect(self): + self.socket.connect((self.ip, self.port)) + + def send(self, data): + self.socket.send(data.encode()) + + def disconnect(self): + self.socket.close() + +def load_config(path=os.path.expanduser("~/.config/lc/lc.conf")): + try: + with open(path, 'rb') as f: + return [PI(ip) for ip in pickle.load(f)] + except FileNotFoundError: + print("Config does not exist") + exit() + + +def save_config(obj, path=os.path.expanduser("~/.config/lc/lc.conf")): + os.makedirs(path[::-1].split('/',1)[-1][::-1], exist_ok=True) + with open(path, 'wb') as f: + pickle.dump(list([o.ip for o in obj]), f) + +@click.command() +@click.argument("arg", nargs=-1) +# @click.option("-s", help="Set HEX Color: -s ffffff") +# @click.option("-v", help="Set HEX Color as base visualizer color: -v ffffff") +def main(arg): + arg = ("help",) if arg == () else arg + match arg[0]: + case "help": + print("lc [help|set|search|list]") + + case "set": + if len(arg) < 2 or not re.match("[0-9a-f]{6}$", arg[1]): + print("color not a valid hex code") + exit() + pi_list = load_config() + for pi in pi_list: + print(pi) + pi.connect() + pi.send(arg[1]) + pi.disconnect() + + case "search": + ip_list = asyncio.run(scan_for_pi()) + pi_list = [PI(ip) for ip in ip_list] + save_config(pi_list) + + case "list": + pi_list = load_config() + if pi_list is not None: + for pi in pi_list: + print(pi) + case "music": + pass + + +if __name__ == '__main__': + main() +# def helpmenu(): + # print("light controll\n") + # print("Options:") + # print("-h show help") + # print("-s set static color") + # print("-v visualizer") + # print("-i interactive interface") + # print("-a ambient light") + # print("-t test function (debug)") + +# def base_color(color): + # return [i // min(color) for i in color] def visualizer(color, amp_strength=0.6): r,g,b = hex_to_rgb(color) @@ -51,8 +122,8 @@ def visualizer(color, amp_strength=0.6): sed = subprocess.Popen(["sed", "-u", "s/;.*;$//"], stdin=cava.stdout, stdout=subprocess.PIPE) for line in sed.stdout: - amp_factor = amp_strength*((int(line)/500)-1)+1 - send(rgb_to_hex(int(r*amp_factor),int(g*amp_factor),int(b*amp_factor))) + amp_factor = amp_strength * ((int(line) / 500) - 1) + 1 + send(rgb_to_hex(int(r * amp_factor), int(g * amp_factor), int(b * amp_factor))) cava.stdout.close() sed.stdout.close() @@ -64,6 +135,7 @@ def visualizer_cava_thread(): for line in sed.stdout: volume_amp = int(line) + print(volume_amp) cava.stdout.close() sed.stdout.close() diff --git a/pi/Makefile b/pi/Makefile new file mode 100644 index 0000000..0ea95c7 --- /dev/null +++ b/pi/Makefile @@ -0,0 +1,6 @@ +install: + chmod +x ./lc.py + cp lc.service /etc/systemd/system/ + systemctl daemon-reload + systemctl enable --now lc.service + systemctl restart lc diff --git a/pi/copy_to_pi.sh b/pi/copy_to_pi.sh new file mode 100755 index 0000000..f51b60a --- /dev/null +++ b/pi/copy_to_pi.sh @@ -0,0 +1,5 @@ +scp lc.py lc.service Makefile pi@192.168.188.76:~/ +ssh pi@192.168.188.76 'sudo make install' +scp lc.py lc.service Makefile pi@192.168.188.64:~/ +ssh pi@192.168.188.76 'sudo make install' + diff --git a/pi/lc.py b/pi/lc.py new file mode 100755 index 0000000..fef7d76 --- /dev/null +++ b/pi/lc.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +import socket +import re +import Adafruit_WS2801 +import Adafruit_GPIO.SPI as SPI +import RPi.GPIO as GPIO + + +PIXEL_COUNT = 32 + +SPI_PORT = 0 +SPI_DEVICE = 0 +pixels = Adafruit_WS2801.WS2801Pixels(PIXEL_COUNT, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE), gpio=GPIO) + +def hex_to_rgb(hex): + r = int(hex[0:2],16) + g = int(hex[2:4],16) + b = int(hex[4:6],16) + return r,g,b + + +reg = re.compile("[0-9a-f]{6}$") + +host = "0.0.0.0" +port = 5000 + +s = socket.socket() +s.bind((host, port)) +s.listen(1) + +while True: + conn, address = s.accept() + print("Connection from: ", str(address)) + + while True: + data = conn.recv(1024).decode() + if not data: + print("Disconneted: ", str(address)) + break + + if reg.match(data): + print("set color", data) + for i in range(PIXEL_COUNT): + pixels.set_pixel_rgb(i, *hex_to_rgb(data)) + pixels.show() + + elif data == 'PING': + conn.send('PONG'.encode()) + else: + print(data, "incorrect format. use hex color code.") + break + conn.close() diff --git a/pi/lc.service b/pi/lc.service new file mode 100644 index 0000000..93cbb8b --- /dev/null +++ b/pi/lc.service @@ -0,0 +1,19 @@ +[Unit] +Description=Start Light Controll as Daemon +After=multi-user.target + +[Service] +Type=simple +User=pi +Group=pi +SyslogIdentifier=lc +Restart=on-failure +RestartSec=2 +WorkingDirectory=/home/pi +ExecStart=/home/pi/lc.py +SyslogIdentifier=pyserver +StandardOutput=syslog +StandardError=syslog + +[Install] +WantedBy=multi-user.target diff --git a/server/color_mode.py b/server/color_mode.py deleted file mode 100644 index 308397e..0000000 --- a/server/color_mode.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys -import time -import ws2801 - -def color_cycle(speed): - if int(speed) == 0: - delay = 0 - else: - delay = 1/int(speed) * 10 - - r = 255 - g = 0 - b = 0 - while True: - while r > 0: - r = r-1 - g = g+1 - ws2801.set_color(r,g,b) - time.sleep(delay) - while g > 0: - g = g-1 - b = b+1 - ws2801.set_color(r,g,b) - time.sleep(delay) - while b > 0: - b = b-1 - r = r+1 - ws2801.set_color(r,g,b) - time.sleep(delay) - -def visualizer(): - for volume in sys.stdin: - volume = int(volume) - ws2801.set_color(volume,0,0) \ No newline at end of file diff --git a/server/copy_to_pi.sh b/server/copy_to_pi.sh deleted file mode 100755 index 8a36831..0000000 --- a/server/copy_to_pi.sh +++ /dev/null @@ -1,2 +0,0 @@ -scp *.py pi@192.168.188.76:~/led_controll/ -scp *.py pi@192.168.188.64:~/led_controll/ diff --git a/server/example/demo.py b/server/example/demo.py deleted file mode 100644 index c4b4d48..0000000 --- a/server/example/demo.py +++ /dev/null @@ -1,101 +0,0 @@ -# Simple demo of of the WS2801/SPI-like addressable RGB LED lights. -import time -import RPi.GPIO as GPIO - -# Import the WS2801 module. -import Adafruit_WS2801 -import Adafruit_GPIO.SPI as SPI - - -# Configure the count of pixels: -PIXEL_COUNT = 32 - -# Alternatively specify a hardware SPI connection on /dev/spidev0.0: -SPI_PORT = 0 -SPI_DEVICE = 0 -pixels = Adafruit_WS2801.WS2801Pixels(PIXEL_COUNT, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE), gpio=GPIO) - - -# Define the wheel function to interpolate between different hues. -def wheel(pos): - if pos < 85: - return Adafruit_WS2801.RGB_to_color(pos * 3, 255 - pos * 3, 0) - elif pos < 170: - pos -= 85 - return Adafruit_WS2801.RGB_to_color(255 - pos * 3, 0, pos * 3) - else: - pos -= 170 - return Adafruit_WS2801.RGB_to_color(0, pos * 3, 255 - pos * 3) - -# Define rainbow cycle function to do a cycle of all hues. -def rainbow_cycle_successive(pixels, wait=0.1): - for i in range(pixels.count()): - # tricky math! we use each pixel as a fraction of the full 96-color wheel - # (thats the i / strip.numPixels() part) - # Then add in j which makes the colors go around per pixel - # the % 96 is to make the wheel cycle around - pixels.set_pixel(i, wheel(((i * 256 // pixels.count())) % 256) ) - pixels.show() - if wait > 0: - time.sleep(wait) - -def rainbow_cycle(pixels, wait=0.005): - for j in range(256): # one cycle of all 256 colors in the wheel - for i in range(pixels.count()): - pixels.set_pixel(i, wheel(((i * 256 // pixels.count()) + j) % 256) ) - pixels.show() - if wait > 0: - time.sleep(wait) - -def rainbow_colors(pixels, wait=0.05): - for j in range(256): # one cycle of all 256 colors in the wheel - for i in range(pixels.count()): - pixels.set_pixel(i, wheel(((256 // pixels.count() + j)) % 256) ) - pixels.show() - if wait > 0: - time.sleep(wait) - -def brightness_decrease(pixels, wait=0.01, step=1): - for j in range(int(256 // step)): - for i in range(pixels.count()): - r, g, b = pixels.get_pixel_rgb(i) - r = int(max(0, r - step)) - g = int(max(0, g - step)) - b = int(max(0, b - step)) - pixels.set_pixel(i, Adafruit_WS2801.RGB_to_color( r, g, b )) - pixels.show() - if wait > 0: - time.sleep(wait) - -def blink_color(pixels, blink_times=5, wait=0.5, color=(255,0,0)): - for i in range(blink_times): - # blink two times, then wait - pixels.clear() - for j in range(2): - for k in range(pixels.count()): - pixels.set_pixel(k, Adafruit_WS2801.RGB_to_color( color[0], color[1], color[2] )) - pixels.show() - time.sleep(0.08) - pixels.clear() - pixels.show() - time.sleep(0.08) - time.sleep(wait) - -def appear_from_back(pixels, color=(255, 0, 0)): - pos = 0 - for i in range(pixels.count()): - for j in reversed(range(i, pixels.count())): - pixels.clear() - # first set all pixels at the begin - for k in range(i): - pixels.set_pixel(k, Adafruit_WS2801.RGB_to_color( color[0], color[1], color[2] )) - # set then the pixel at position j - pixels.set_pixel(j, Adafruit_WS2801.RGB_to_color( color[0], color[1], color[2] )) - pixels.show() - time.sleep(0.02) - - -if __name__ == "__main__": - while True: - rainbow_colors(pixels, wait=0.3) - diff --git a/server/example/rainbow.py b/server/example/rainbow.py deleted file mode 100644 index 9f9bb95..0000000 --- a/server/example/rainbow.py +++ /dev/null @@ -1,46 +0,0 @@ -import time -import RPi.GPIO as GPIO - -import Adafruit_WS2801 -import Adafruit_GPIO.SPI as SPI - -PIXEL_COUNT = 32 - -SPI_PORT = 0 -SPI_DEVICE = 0 -pixels = Adafruit_WS2801.WS2801Pixels(PIXEL_COUNT, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE), gpio=GPIO) - -def set_color(r, g, b): - for i in range(pixels.count()): - pixels.set_pixel(i, Adafruit_WS2801.RGB_to_color(r,g,b)) - pixels.show() - -def clear_pixels(): - pixels.clear() - pixels.show() - -def main(): - DELAY = 0.1 - r = 255 - g = 0 - b = 0 - - while True: - while r > 0: - r = r-1 - g = g+1 - set_color(r,g,b) - time.sleep(DELAY) - while g > 0: - g = g-1 - b = b+1 - set_color(r,g,b) - time.sleep(DELAY) - while b > 0: - b = b-1 - r = r+1 - set_color(r,g,b) - time.sleep(DELAY) - -if __name__ == "__main__": - main() diff --git a/server/led_controll.py b/server/led_controll.py deleted file mode 100755 index 124d866..0000000 --- a/server/led_controll.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python3 -import sys -import getopt - -import ws2801 -import server -import color_mode - -def helpmenu(): - print("Options:") - print("-h show help") - print("-s color in hex") - print("-c colorcycle") - -def main(argv): - - if not sys.stdin.isatty(): - color_mode.visualizer() - sys.exit() - - try: - opts, args = getopt.getopt(argv,"hds:c:r") - except getopt.GetoptError: - print("ws2801.py: invalid option") - print("Try 'ws2801.py -h' for help") - sys.exit(2) - - for opt, arg in opts: - if opt == "-h": - helpmenu() - sys.exit() - elif opt == "-s": - r,g,b = ws2801.hex_to_rgb(arg) - ws2801.set_color(r,g,b) - sys.exit() - elif opt == "-c": - color_mode.color_cycle(arg) - sys.exit() - elif opt == "-d": - server.start() - sys.exit() - - helpmenu() - sys.exit() - -if __name__ == "__main__": - main(sys.argv[1:]) diff --git a/server/requirements.txt b/server/requirements.txt deleted file mode 100644 index c8cbd9b..0000000 --- a/server/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -RPi.GPIO -Adafruit-WS2801 -Adafruit-GPIO - - diff --git a/server/server.py b/server/server.py deleted file mode 100644 index 2c3a07e..0000000 --- a/server/server.py +++ /dev/null @@ -1,33 +0,0 @@ -import socket -import ws2801 -import re - -def validate_data(data): - reg = re.compile("[0-9a-f]{6}$") - return reg.match(data) - -def start(): - host = "0.0.0.0" - port = 5000 - - server_socket = socket.socket() - server_socket.bind((host, port)) - - # configure how many client the server can listen simultaneously - server_socket.listen(1) - while True: - conn, address = server_socket.accept() # accept new connection - print("Connection from: " + str(address)) - # receive data stream. it won't accept data packet greater than 1024 bytes - while True: - data = conn.recv(1024).decode() - if not data: - print("Disconnected: " + str(address)) - break - if validate_data(data): - r,g,b = ws2801.hex_to_rgb(data) - ws2801.set_color(r,g,b) - else: - print("incorrect format. use hex color code.") - conn.close() # close the connection - diff --git a/server/ws2801.py b/server/ws2801.py deleted file mode 100644 index bb03370..0000000 --- a/server/ws2801.py +++ /dev/null @@ -1,25 +0,0 @@ -import RPi.GPIO as GPIO -import Adafruit_WS2801 -import Adafruit_GPIO.SPI as SPI - -PIXEL_COUNT = 32 - -SPI_PORT = 0 -SPI_DEVICE = 0 -pixels = Adafruit_WS2801.WS2801Pixels(PIXEL_COUNT, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE), gpio=GPIO) - -def hex_to_rgb(hex): - r = int(hex[0:2],16) - g = int(hex[2:4],16) - b = int(hex[4:6],16) - print(r,g,b) - return r,g,b - -def set_color(r,g,b): - for i in range(pixels.count()): - pixels.set_pixel(i, Adafruit_WS2801.RGB_to_color(r,g,b)) - pixels.show() - -def clear_pixels(): - pixels.clear() - pixels.show()