-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
77265fc
commit 6174f69
Showing
23 changed files
with
2,943 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Auto detect text files and perform LF normalization | ||
* text=auto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
--- | ||
name: Bug report | ||
about: Create a report to help improve this project | ||
title: '' | ||
labels: 'bug' | ||
assignees: '' | ||
|
||
--- | ||
|
||
**Describe the bug** | ||
A clear and concise description of what the bug is. | ||
|
||
**To Reproduce** | ||
Steps to reproduce the behavior: | ||
1. Go to '...' | ||
2. Click on '...' | ||
3. Scroll down to '...' | ||
4. See error | ||
|
||
**Expected behavior** | ||
A clear and concise description of what you expected to happen. | ||
|
||
**Screenshots and videos** | ||
If possible, add screenshots or videos to help explain your problem. | ||
|
||
**Additional context** | ||
Add any other context about the problem here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
*.pyc | ||
*.bsdesign | ||
*.blend1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
from datetime import datetime | ||
import time | ||
|
||
# Returns the current time (timezone sensitive) in the format [hour, minute, second] | ||
# where each value is an integer | ||
def getTime(): | ||
time = str(datetime.now()).split(" ")[1].split(".")[0] | ||
time_array = time.split(":") | ||
|
||
for i in range(len(time_array)): | ||
time_array[i] = int(time_array[i]) | ||
|
||
return time_array | ||
|
||
# Formats the getTime output into "hh:mm:ss" | ||
def formatTime(time_array, hour = False, minute = False, second = False, ampm = False): | ||
# Don't alter original data | ||
time_array = time_array.copy() | ||
|
||
time_value = "" | ||
# Remove any values that user doesn't want | ||
if second == False: | ||
time_array.pop(2) | ||
if minute == False: | ||
time_array.pop(1) | ||
if hour == False: | ||
time_array.pop(0) | ||
elif ampm == True: # If user wants it in 24-hour time | ||
if time_array[0] >= 12: | ||
time_array[0] -= 12 | ||
time_value = "PM" | ||
else: | ||
time_value = "AM" | ||
|
||
if time_array[0] == 0: | ||
time_array[0] = 12 | ||
|
||
for i in range(len(time_array)): | ||
time_array[i] = str(time_array[i]) | ||
|
||
time = f"{':'.join(time_array)} {time_value}" | ||
|
||
return time | ||
|
||
# Returns a string of today's day | ||
def getDay(): | ||
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] | ||
day = datetime.weekday(datetime.now()) | ||
return days[day], day | ||
|
||
on_days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] | ||
on_time_range = [[8, 0, 0], [16, 30, 0]] | ||
|
||
|
||
# Startup information | ||
print(f"\n{formatTime(getTime(), hour=True, minute=True, second=True, ampm=True)}") | ||
print(getDay()[0]) | ||
|
||
# Main loop | ||
while True: | ||
# Get today's day | ||
this_day = getDay() | ||
run_check = False | ||
|
||
# While it's a weekday | ||
while this_day[0] in on_days: | ||
# Get current time | ||
this_time = getTime() | ||
print(f"\nCurrent time: {formatTime(this_time, hour=True, minute=True, second=True, ampm=True)}") | ||
|
||
# Checks if current time and day is within valid range | ||
run_check = False | ||
if (this_day[0] in on_days) and ((this_time[0] > on_time_range[0][0] and this_time[0] < on_time_range[1][0]) or ((this_time[0] == on_time_range[0][0]) and ((this_time[1] > on_time_range[0][1]) or ((this_time[1] == on_time_range[0][1]) and (this_time[2] >= on_time_range[0][2])))) or ((this_time[0] == on_time_range[1][0]) and ((this_time[1] < on_time_range[1][1]) or ((this_time[1] == on_time_range[1][1]) and (this_time[2] < on_time_range[1][2]))))): | ||
run_check = True | ||
|
||
# If it's supposed to be displaying | ||
if run_check == True: | ||
# Display anything in here | ||
pass | ||
else: | ||
time.sleep(60) | ||
|
||
# While it's a weekend | ||
while this_day[0] not in on_days: | ||
this_day = getDay() | ||
print(f"\nStopped for 24 hours because today is {this_day[0]}.\nWait until one of these days: {on_days}.") | ||
|
||
# Don't do anything for 7h59m59s | ||
# I chose that specific number so that there's enough time for it to | ||
# recheck the day and go into normal operation by 8am. | ||
time.sleep(28799) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2022 Sooraj Sannabhadti | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# LED Light Wall Project | ||
|
||
## Contents | ||
|
||
1. [Contents](#contents) | ||
2. [About](#about) | ||
3. [Installation](#installation) | ||
4. [Usage](#usage)\ | ||
a. [Physical Version](#physical-version)\ | ||
b. [Testing Version](#testing-version) | ||
5. [Features](#features) | ||
|
||
## About | ||
|
||
This is a passion project, made independently, and exclusively by two high school students: [WhenLifeHandsYouLemons](https://github.com/WhenLifeHandsYouLemons) and [DreamingElectricSheep](https://github.com/DreamingElectricSheep). | ||
|
||
This project is our creation of an LED light wall-- Essentially a large screen that is able to display pretty much anything-- just like the screens on your phone, computer or TV. We've specialized ours to act as a sort of visual spectacle, inpired by the likes of Fireworks, Lazershows, and Nanoleafs. Our LED light wall displays a continous flow of random [patterns](#features)-- largely inspired by natural patterns like waves, which can also be controlled via ultrasonic sensors. This GitHub repository contains all of the code and documentation throughout the development of our project. | ||
|
||
This enourmous project took more than a year to complete, covering far more than just the scope of Computer Science and Programming-- Enginnering, Design, Electronics, Modeling, Planning and of course, Programming and Mathematics were all integral processes in the creation of this project. | ||
|
||
The documentation is hosted at: <https://whenlifehandsyoulemons.github.io/LED-Light-Wall/>. | ||
|
||
This project was initially in a private repository and on May 24, 2023, it was moved to a new public repository and so much of the git history isn't present here. | ||
|
||
## Installation | ||
|
||
To install this project, you will need to clone the repository and then install the dependencies. To do this, run the following commands on a Raspberry Pi (RPi 3 or newer with Raspberry Pi OS installed): | ||
|
||
```console | ||
git clone https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall.git | ||
cd LED-Light-Wall | ||
pip install requirements.txt | ||
``` | ||
|
||
## Usage | ||
|
||
### Physical Version | ||
|
||
To run this project with a physical board, you will need to run the `rpi_main.py` file. To do this, run the following command: | ||
|
||
```console | ||
cd LED-Light-Wall | ||
sudo python3 rpi_main.py | ||
``` | ||
|
||
**Note**: If you're using the [Thonny IDE](https://thonny.org/), you need to run the `rpi_main.py` file in sudo mode. To do so, run the `open_sudo_thonny.py` file first (located inside the "*RPi Files*" folder), then open the `rpi_main.py` file inside Thonny and run it. | ||
|
||
### Testing Version | ||
|
||
To run the testing version, you will need to run the `pygame_main.py` file (located inside the "*Testing Version*" folder). To do this, run the following command: | ||
|
||
```console | ||
cd LED-Light-Wall/Testing\ Version | ||
python pygame_main.py | ||
``` | ||
|
||
**Note**: The testing version lacks full parity with the physical version. It is only used to test patterns that can be precomputed. Images, text, ultrasonic sensors, and graphics are not supported in the testing version. The testing version doesn't include the same number of erroneous data checks as the physical version so unseen errors may occur when porting to the physical version. | ||
|
||
## Features | ||
|
||
- Displays a multitude of extensively customizable patterns and graphics, both continious and static: | ||
- [Circular waves](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/precomputations.py#L83) | ||
- [Square waves](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/precomputations.py#L18) | ||
- [Normal waves](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/precomputations.py#L162) | ||
- [Rain drops](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/precomputations.py#L197) | ||
- [Lines](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/precomputations.py#L210) | ||
- [Circles](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/graphics.py#L40) | ||
- [Rectangles](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/graphics.py#L31) | ||
- [Scrolling text](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/graphics.py#L51) | ||
- [Images](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/sblob/main/images.py) | ||
- [Ultrasonic sensors](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/ultrasonics.py) | ||
- Uses ultrasonic sensors to detect when an object is near the board and starts a different pattern. | ||
- [Power saving mode](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/blob/main/rpi_main.py#L111) | ||
- Turns off the display on certain days and at certain times to conserve power. | ||
- [Includes a testing version](https://github.com/WhenLifeHandsYouLemons/LED-Light-Wall/tree/main/Testing%20Version) | ||
- Can be used to test patterns without the need for a physical board. | ||
|
||
[Back to top](#led-light-wall-project) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import os | ||
os.system("sudo thonny") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
""" | ||
Constants | ||
""" | ||
# Variables for Pygame | ||
BOARD_WIDTH = 30 | ||
BOARD_HEIGHT = 20 | ||
PIXEL_SIZE = 20 | ||
GAP_SIZE = 5 | ||
|
||
# Pygame window | ||
BACKGROUND_COLOUR = 7, 7, 7 | ||
WINDOW_HEIGHT = (PIXEL_SIZE + GAP_SIZE) * BOARD_HEIGHT | ||
WINDOW_WIDTH = (PIXEL_SIZE + GAP_SIZE) * BOARD_WIDTH | ||
RUNNING_WINDOW = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
""" | ||
Main loop | ||
""" | ||
import sys | ||
import pygame | ||
from datetime import datetime | ||
import time | ||
|
||
# Custom imports | ||
from pygame_constants import * | ||
from pygame_utilities import * | ||
from pygame_precomputations import * | ||
|
||
|
||
# Returns the current time (timezone sensitive) in the format [hour, minute, second] | ||
# where each value is an integer | ||
def getTime(): | ||
time = str(datetime.now()).split(" ")[1].split(".")[0] | ||
time_array = time.split(":") | ||
|
||
for i in range(len(time_array)): | ||
time_array[i] = int(time_array[i]) | ||
|
||
return time_array | ||
|
||
# Formats the getTime output into "hh:mm:ss" | ||
def formatTime(time_array, hour = False, minute = False, second = False, ampm = False): | ||
# Don't alter original data | ||
time_array = time_array.copy() | ||
|
||
time_value = "" | ||
# Remove any values that user doesn't want | ||
if second == False: | ||
time_array.pop(2) | ||
if minute == False: | ||
time_array.pop(1) | ||
if hour == False: | ||
time_array.pop(0) | ||
elif ampm == True: # If user wants it in 24-hour time | ||
if time_array[0] >= 12: | ||
time_array[0] -= 12 | ||
time_value = "PM" | ||
else: | ||
time_value = "AM" | ||
|
||
if time_array[0] == 0: | ||
time_array[0] = 12 | ||
|
||
for i in range(len(time_array)): | ||
time_array[i] = str(time_array[i]) | ||
|
||
time = f"{':'.join(time_array)} {time_value}" | ||
|
||
return time | ||
|
||
# Returns a string of today's day | ||
def getDay(): | ||
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] | ||
day = datetime.weekday(datetime.now()) | ||
return days[day], day | ||
|
||
# Allowed time and days | ||
on_days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] | ||
# Uses 24-hour time format | ||
on_time_range = [[8, 0, 0], [16, 30, 0]] | ||
|
||
# Reset board | ||
setAllPixelsColour(COLOURS["Black"]) | ||
|
||
# Startup information | ||
print("\nStarting display.") | ||
print(f"\n{formatTime(getTime(), hour=True, minute=True, second=True, ampm=True)}") | ||
print(getDay()[0]) | ||
|
||
# Main loop | ||
while RUNNING_WINDOW: | ||
clock.tick(60) | ||
|
||
# Get today's day | ||
this_day = getDay() | ||
run_check = False | ||
|
||
# While it's a weekday | ||
while this_day[0] in on_days and RUNNING_WINDOW: | ||
# Get current time | ||
this_time = getTime() | ||
print(f"\nCurrent time: {formatTime(this_time, hour=True, minute=True, second=True, ampm=True)}") | ||
|
||
# Checks if current time and day is within valid range | ||
run_check = False | ||
# I've shortened it to hopefully make it check faster | ||
if (this_day[0] in on_days) and ((this_time[0] > on_time_range[0][0] and this_time[0] < on_time_range[1][0]) or ((this_time[0] == on_time_range[0][0]) and ((this_time[1] > on_time_range[0][1]) or ((this_time[1] == on_time_range[0][1]) and (this_time[2] >= on_time_range[0][2])))) or ((this_time[0] == on_time_range[1][0]) and ((this_time[1] < on_time_range[1][1]) or ((this_time[1] == on_time_range[1][1]) and (this_time[2] < on_time_range[1][2]))))): | ||
run_check = True | ||
|
||
# If it's supposed to be displaying | ||
if run_check == True: | ||
# Do stuff | ||
print("\nCalculating waves...") | ||
merged, non_merged = randomisePatterns() | ||
|
||
print("Displaying waves.") | ||
displayRandomPatterns(merged, non_merged, 0.05) | ||
|
||
setAllPixelsColour(COLOURS["Black"]) | ||
|
||
pygame.display.update() | ||
else: | ||
# Wait for 5 minutes before rechecking the day and time | ||
time.sleep(60 * 5) | ||
|
||
pygame.display.update() | ||
|
||
for event in pygame.event.get(): | ||
if event.type == pygame.QUIT: | ||
RUNNING_WINDOW = False | ||
pygame.quit() | ||
|
||
# While it's a weekend | ||
while this_day[0] not in on_days and RUNNING_WINDOW: | ||
print(f"\nStopped for 8 hours because today is {this_day[0]}.\nWait until one of these days: {on_days}.") | ||
|
||
# Don't do anything for the minimum amount of time possible - 10 seconds | ||
# I choose the number so that there's enough time for it to | ||
# recheck the day and go into normal operation by it's specified on time. | ||
time.sleep((on_time_range[0][0] * 60 * 60) + (on_time_range[0][1] * 60) + (on_time_range[0][2]) - 10) | ||
|
||
# Recheck the day after sleeping | ||
# This fixes an issue where it would get the day but finish the loop | ||
# once more before exiting, which meant it sleeps for 8 hours on Monday | ||
this_day = getDay() | ||
|
||
sys.exit() |
Oops, something went wrong.