Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
micropython/
target/
src/settings.py
src/phew
__pycache__
temp/
#OS garbage
.DS_STORE
.DS_STORE
49 changes: 49 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Project Credits and Licensing Notices

This project is primarily licensed under the **GNU General Public License v3.0 (GPLv3)**.
It incorporates third-party open-source software as detailed below.

---

## Microdot Web Framework

This project uses the **Microdot** web framework for its server implementation.

- **Author:** Miguel Grinberg
- **Source:** [https://github.com/miguelgrinberg/microdot](https://github.com/miguelgrinberg/microdot)
- **License:** MIT License

### MIT License (Microdot)

Copyright (c) 2019 Miguel Grinberg

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.

---

## Additional Acknowledgments

- **utemplate:** Included within the Microdot library distribution.
- **Author:** Paul Sokolovsky
- **License:** MIT License

---

*Note: While individual components remain under their respective licenses,
the combined work as a whole is distributed under the terms of the GPLv3.*
33 changes: 19 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@
clean: ##Nukes the target and micropython dirs
rm -rf target/
rm -rf micropython/
-rm -rf src/phew
-rm -rf temp/

.PHONY: setup-python-env
setup-python-env: ## Setups python dev environment
pyenv install $(cat .python-version)
pyenv local $(cat .python-version)
@eval "$$(pyenv init -)" && pyenv virtualenv $(cat .python-version) gunpla
@eval "$$(pyenv init -)" && pyenv activate gunpla && pip install --require-virtualenv -r requirements.txt

.PHONY: setup
setup: ## Downloads and setups required dependencies
mkdir micropython
wget -P micropython https://micropython.org/resources/firmware/rp2-pico-w-20230426-v1.20.0.uf2
mkdir temp
wget -P temp/phew https://github.com/pimoroni/phew/archive/refs/tags/v0.0.3.zip
unzip temp/phew/v0.0.3.zip -d temp/
mv temp/phew-0.0.3/phew/ src/
cp src/config.py.template src/settings.py
pyenv install $(cat .python-version)
pyenv local $(cat .python-version)
@eval "$$(pyenv init -)" && pyenv virtualenv $(cat .python-version) gunpla
@eval "$$(pyenv init -)" && pyenv activate gunpla && pip install --require-virtualenv -r requirements.txt

.PHONY: install-micropython-ubuntu
install-micropython-ubuntu: ## Installs micropython to pi board on ubuntu
Expand Down Expand Up @@ -49,17 +48,23 @@ deploy: ## Deploys the built artifacts to the pi board
rshell rm -r /pyboard/*
rshell cp -r target/* /pyboard/

#python tooling
.PHONY: format-other
format-other: ## Formats anything else
markdownlint -c .markdownlint.yaml --fix **/*.md

.PHONY: format
format: ## Format the Python code
autopep8 -i -r src/
format: format-python format-other ## Formats everything

#python tooling
.PHONY: format-python
format-python: ## Format the Python code
autopep8 -i -r src/ tests/
isort .

.PHONY: lint
lint: ## Lints the python code and documents
markdownlint --fix **/*.md
pylint src/ --ignore src/phew

markdownlint -c .markdownlint.yaml **/*.md
pylint src/ --ignore Microdot.py

help: ## Show this help.
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
13 changes: 9 additions & 4 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import uasyncio

import src
from src import settings
from src.webserver import WebServer
from src.hardware.Hardware import Hardware
from src.server.webserver import WebServer


def main():
webserver = WebServer(settings.webserver)
webserver.main()
hardware: Hardware = src.hardware.get_hardware()
webserver = WebServer(settings.webserver, hardware)
uasyncio.run( webserver.run())


if __name__ == "__main__":
main()
main()
60 changes: 23 additions & 37 deletions src/gunpla/base_gundam.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import json

from src.phew import server
from src.phew.server import Request, Response, logging
from src.pi.board_led import BoardLED
from src.pi.disabled_LED import DisabledLED
from src.pi.LED import LED
from src.server.Wrappers import safe_execution


class BaseGundam:
"""
Base Gunpla.
"""
board_led = BoardLED()

def __init__(self):
with open(self.get_config_file()) as config_contents:
Expand All @@ -24,40 +21,31 @@ def get_config_file(self) -> str:
"""
raise Exception("Not implemented")

def led_on(self, request: Request, led_name: str) -> Response:
def led_on(self, led_name: str):
"""
Turns a Single LED on by name
"""
logging.info(f"turning on {led_name}")
try:
led = self._get_led_from_name(led_name)
led.on()
return Response(f"{led_name}: on", 200)
except Exception as ex:
return Response(str(ex), 500)
print(f"turning on {led_name}")
led = self._get_led_from_name(led_name)
led.on()

def led_off(self, request: Request, led_name: str) -> Response:
def led_off(self, led_name: str) -> None:
"""
Turns a single LED off by name
"""
logging.info(f"turning off {led_name}")
try:
led = self._get_led_from_name(led_name)
led.off()
return Response(f"{led_name}: off", 200)
except Exception as ex:
return Response(str(ex), 500)
print(f"turning off {led_name}")
led = self._get_led_from_name(led_name)
led.off()

# TODO: make this not need the safe_execution and do it when we register paths

def all_on(self, request: Request) -> Response:
@safe_execution
def all_on(self) -> None:
"""
Turns all configured LED's on.
"""
logging.info("turning on all leds")
try:
leds = self._all_leds_on()
return Response(f"<html>All on\n {leds} </html>", 200)
except Exception as ex:
return Response(str(ex), 500)
print("turning on all leds")
self._all_leds_on()

def _all_leds_on(self) -> str:
"""
Expand All @@ -74,16 +62,14 @@ def _all_leds_on(self) -> str:
leds += f"{led_name}: on\n"
return leds

def all_off(self, request: Request) -> Response:
# TODO: make this not need the safe_execution and do it when we register paths
@safe_execution
def all_off(self) -> None:
"""
Turns all configured LED's off
"""
logging.info("turning off all leds")
try:
leds = self._all_leds_off()
return Response("All off\n" + leds, 200)
except Exception as ex:
return Response(str(ex), 500)
print("turning off all leds")
self._all_leds_off()

def _all_leds_off(self) -> str:
""""
Expand All @@ -100,14 +86,14 @@ def _all_leds_off(self) -> str:
leds += f"{led_name}: off\n"
return leds

def get_all_leds(self, filter: list[str] = []) -> list[LED]:
def get_all_leds(self, ignore_list: list[str] = []) -> list[LED]:
"""
Returns all LEDs configured, enabled or disabled. But not the board_led
"""
leds = []
for led_entry in self.config['leds']:
led_name = led_entry['name']
if led_name in filter:
if led_name in ignore_list:
continue
led = self._get_led_from_name(led_name)
leds.append(led)
Expand All @@ -122,7 +108,7 @@ def _get_led_from_name(self, led_name: str) -> LED:
"""
entry = self.__get_entry_from_name(led_name)
if 'disabled' in entry and entry['disabled']:
logging.debug(f"{led_name} is disabled")
print(f"{led_name} is disabled")
return DisabledLED(led_name)
return LED(entry['pin'], led_name)

Expand Down
45 changes: 13 additions & 32 deletions src/gunpla/nu_gundam.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import random
import time

import uasyncio

from src.gunpla.base_gundam import BaseGundam
from src.phew.server import Request, Response
from src.pi import LED
from src.pi.led_effect import LEDEffects


Expand All @@ -18,40 +17,27 @@ def get_config_file(self) -> str:
"""
return "src/config/nu_gundam.json"

def activation(self, request: Request) -> Response:
async def activation(self) -> None:
"""
Runs the activation lightshow
this is just a sample test
"""
head_led = self._get_led_from_name("head")
head_led.on()
time.sleep(0.1)
await uasyncio.sleep(0.1)
head_led.off()
time.sleep(0.5)
LEDEffects.brighten(head_led)
return Response("finished", 200)
await uasyncio.sleep(0.5)
await LEDEffects.brighten(head_led)

def fire_funnels(self, request: Request) -> Response:
async def fire_funnels(self) -> None:
"""
Light Show that fires fin funnels in order
"""
fin1: LED = self._get_led_from_name("fin_funnel_1")
fin2: LED = self._get_led_from_name("fin_funnel_2")
fin3: LED = self._get_led_from_name("fin_funnel_3")
fin4: LED = self._get_led_from_name("fin_funnel_4")
fin5: LED = self._get_led_from_name("fin_funnel_5")
fin6: LED = self._get_led_from_name("fin_funnel_6")

LEDEffects.fire(fin1)
LEDEffects.fire(fin2)
LEDEffects.fire(fin3)
LEDEffects.fire(fin4)
LEDEffects.fire(fin5)
LEDEffects.fire(fin6)

return Response("finished", 200)
for i in range(1, 7):
funnel = self._get_led_from_name(f"fin_funnel_{i}")
await LEDEffects.fire(funnel)

def random_funnels(self, request: Request) -> Response:
async def random_funnels(self) -> None:
"""
Randomly fires fin funnels that are enabled for an infinite amount of time
This currently does not end and needs thread management to properly be able to be halted.
Expand All @@ -63,12 +49,7 @@ def random_funnels(self, request: Request) -> Response:
# Filter out funnels that are disabled.
funnels = [funnel for funnel in funnels if funnel.enabled()]

if not funnels:
return Response("No funnels can be fired", 400)

while True:
funnel = random.choice(funnels)
LEDEffects.charge_fire(funnel)
time.sleep(random.uniform(0, 3))

return Response("finished", 200)
await LEDEffects.charge_fire(funnel)
await uasyncio.sleep(random.uniform(0, 3))
10 changes: 4 additions & 6 deletions src/gunpla/unicorn_banshee.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import time
import uasyncio

from src.gunpla.base_gundam import BaseGundam
from src.phew.server import Request, Response
from src.pi.led_effect import LEDEffects


Expand All @@ -16,11 +15,10 @@ def get_config_file(self) -> str:
"""
return "src/config/unicorn_banshee.json"

def glow(self, request: Request) -> Response:
async def glow(self) -> None:
"""
Runs the glow lightshow
"""
LEDEffects.brighten_all(self.get_all_leds())
time.sleep(3)
await LEDEffects.brighten_all(self.get_all_leds())
await uasyncio.sleep(3)
self._all_leds_off()
return Response("finished", 200)
25 changes: 25 additions & 0 deletions src/hardware/Hardware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

from src.hardware.Networking import Networking
from src.pi.board_led import BoardLED


class Hardware:
"""
Hardware abstraction layer.
"""

def get_pin(self, pin_num, mode):
raise NotImplementedError

def get_pwm(self, pin_obj):
raise NotImplementedError

def reset_pin(self, pin_num):
raise NotImplementedError

@property
def networking(self) -> Networking:
raise NotImplementedError

def board_led(self) -> BoardLED:
raise NotImplementedError
Loading