From c4826219bf89f76e3f1c0e09c7bd34fb69346905 Mon Sep 17 00:00:00 2001 From: Konstantin Koslowski Date: Sun, 11 Apr 2021 21:22:16 +0200 Subject: [PATCH] light: initial commit --- .gitignore | 147 ++++++++++++++++++++++++++++++++++ .pylintrc | 22 ++++++ .vscode/extensions.json | 9 +++ .vscode/settings.json | 23 ++++++ dev-requirements.txt | 1 + micropy.json | 14 ++++ pymakr.conf | 21 +++++ requirements.txt | 0 src/boot.py | 1 + src/main.py | 169 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 407 insertions(+) create mode 100644 .gitignore create mode 100644 .pylintrc create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 dev-requirements.txt create mode 100644 micropy.json create mode 100644 pymakr.conf create mode 100644 requirements.txt create mode 100644 src/boot.py create mode 100644 src/main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..df2f3f7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,147 @@ + +# Created by https://www.gitignore.io/api/python,visualstudiocode +# Edit at https://www.gitignore.io/?templates=python,visualstudiocode + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +# End of https://www.gitignore.io/api/python,visualstudiocode + +### Micropy Cli ### +.micropy/ +!micropy.json +!src/lib \ No newline at end of file diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..8049e2e --- /dev/null +++ b/.pylintrc @@ -0,0 +1,22 @@ +[MASTER] +# Loaded Stubs: esp8266-micropython-1.11.0 +init-hook='import sys;sys.path[1:1]=["src/lib",".micropy/BradenM-micropy-stubs-c89b5ef/frozen", ".micropy/BradenM-micropy-stubs-e1b8ce6/frozen", ".micropy/BradenM-micropy-stubs-c89b5ef/stubs", ".micropy/light", ]' + +[MESSAGES CONTROL] +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". + +disable = missing-docstring, line-too-long, trailing-newlines, broad-except, logging-format-interpolation, invalid-name, empty-docstring, + no-method-argument, assignment-from-no-return, too-many-function-args, unexpected-keyword-arg + # the 2nd line deals with the limited information in the generated stubs. \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..2e8d346 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-python.python", // micropy-cli: required for vscode micropython integrations + "VisualStudioExptTeam.vscodeintellicode" // micropy-cli: optional for advanced intellisense + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..96bcfdf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + "python.linting.enabled": true, + "python.jediEnabled": false, + "python.autoComplete.extraPaths": [ + ".micropy/BradenM-micropy-stubs-c89b5ef/frozen", + ".micropy/BradenM-micropy-stubs-e1b8ce6/frozen", + ".micropy/BradenM-micropy-stubs-c89b5ef/stubs", + ".micropy/light" + ], + "python.autoComplete.typeshedPaths": [ + ".micropy/BradenM-micropy-stubs-c89b5ef/frozen", + ".micropy/BradenM-micropy-stubs-e1b8ce6/frozen", + ".micropy/BradenM-micropy-stubs-c89b5ef/stubs", + ".micropy/light" + ], + "python.analysis.typeshedPaths": [ + ".micropy/BradenM-micropy-stubs-c89b5ef/frozen", + ".micropy/BradenM-micropy-stubs-e1b8ce6/frozen", + ".micropy/BradenM-micropy-stubs-c89b5ef/stubs", + ".micropy/light" + ], + "python.linting.pylintEnabled": true +} \ No newline at end of file diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..0e59408 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1 @@ +micropy-cli diff --git a/micropy.json b/micropy.json new file mode 100644 index 0000000..20edd41 --- /dev/null +++ b/micropy.json @@ -0,0 +1,14 @@ +{ + "name": "light", + "stubs": { + "esp8266-micropython-1.11.0": "1.2.0" + }, + "dev-packages": { + "micropy-cli": "*" + }, + "packages": {}, + "config": { + "vscode": true, + "pylint": true + } +} \ No newline at end of file diff --git a/pymakr.conf b/pymakr.conf new file mode 100644 index 0000000..e027b28 --- /dev/null +++ b/pymakr.conf @@ -0,0 +1,21 @@ +{ + "address": "/dev/ttyUSB0", + "username": "micro", + "password": "python", + "sync_folder": "src", + "open_on_start": true, + "safe_boot_on_upload": false, + "py_ignore": [ + "pymakr.conf", + ".vscode", + ".gitignore", + ".git", + "project.pymakr", + "env", + "venv", + ".python-version", + ".micropy/", + "micropy.json" + ], + "fast_upload": false +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/boot.py b/src/boot.py new file mode 100644 index 0000000..a328c7a --- /dev/null +++ b/src/boot.py @@ -0,0 +1 @@ +# boot.py - - runs on boot-up \ No newline at end of file diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..38e95b4 --- /dev/null +++ b/src/main.py @@ -0,0 +1,169 @@ +# main.py +import machine +import neopixel +import time + +# Pins +PIN_NP = 5 +PIN_Y= 0 +PIN_G= 4 +PIN_B= 2 +# NeoPixel +NUM_LED = 16 +# y - Mode +MODE_MAX = 5 +MODES = [ "off", "on", "color_chase", "rainbow", "strobe" ] +MODE_STEP = 1 +# g - Color +COLOR_MAX = 255 +COLOR_STEP = 5 +# b - Brightness +BRIGHTNESS_MAX = 250 +BRIGHTNESS_STEP = 50 +# Delay +DELAY = 0.1 +DELAY_IRQ = 0.5 +DELAY_CHASE = 0.05 +DELAY_STROBE = 0.2 + +light = None +state = None + +def handle_y(pin): + global light, state + if state is None: + state = machine.disable_irq() + light.set_fn("mode") + +def handle_g(pin): + global light, state + light.set_fn("color") + +def handle_b(pin): + global light, state + light.set_fn("brightness") + +class Light: + def __init__(self): + self.mode = 0 + self.color = 0 + self.brightness = 0 + self.np = neopixel.NeoPixel(machine.Pin(PIN_NP), NUM_LED) + self.sw_y = machine.Pin(PIN_Y, machine.Pin.IN, machine.Pin.PULL_UP) + self.sw_g = machine.Pin(PIN_G, machine.Pin.IN, machine.Pin.PULL_UP) + self.sw_b = machine.Pin(PIN_B, machine.Pin.IN, machine.Pin.PULL_UP) + self.sw_y.irq(trigger=machine.Pin.IRQ_FALLING, handler=handle_y) + self.sw_g.irq(trigger=machine.Pin.IRQ_FALLING, handler=handle_g) + self.sw_b.irq(trigger=machine.Pin.IRQ_FALLING, handler=handle_b) + + def set_fn(self, fn): + if fn == "mode": + self.mode = (self.mode + MODE_STEP) % MODE_MAX + print("set fn {} to {}".format(fn, MODES[self.mode])) + elif fn == "color": + self.color = (self.color + COLOR_STEP) % COLOR_MAX + print("set fn {} to {}".format(fn, self.color)) + elif fn == "brightness": + self.brightness = (self.brightness + BRIGHTNESS_STEP) % BRIGHTNESS_MAX + print("set fn {} to {}".format(fn, self.brightness)) + else: + print("invalid fn {}".format(fn)) + + def set_color(self, color): + for i in range(NUM_LED): + self.np[i] = self.wheel(color) + self.np.write() + + 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 start(self): + global state + old_mode = self.mode + old_color = self.color + old_brightness = self.brightness + i = 0 + j = 0 + while True: + if state: + time.sleep(DELAY_IRQ) + machine.enable_irq(state) + state = None + if not self.mode == old_mode: + print("mode: {}, old_mode: {}".format(MODES[self.mode], MODES[old_mode])) + if MODES[self.mode] == "off": + if not self.mode == old_mode: + old_mode = self.mode + self.set_color(-1) + time.sleep(DELAY) + # Off + elif MODES[self.mode] == "on": + if not self.mode == old_mode: + old_mode = self.mode + self.set_color(self.color) + if not self.color == old_color: + old_color = self.color + self.set_color(self.color) + time.sleep(DELAY) + # On + elif MODES[self.mode] == "color_chase": + if not self.mode == old_mode: + old_mode = self.mode + self.set_color(-1) + i = 0 + j = 0 + k = 0 + if not self.color == old_color: + old_color = self.color + self.np[i] = self.wheel(self.color) + self.np[(i+1) % NUM_LED] = self.wheel(self.color) + self.np[(i+2) % NUM_LED] = self.wheel(self.color) + self.np.write() + self.np[i] = self.wheel(-1) + i = (i + 1) % NUM_LED + time.sleep(DELAY_CHASE) + # Rainbow + elif MODES[self.mode] == "rainbow": + if not self.mode == old_mode: + old_mode = self.mode + i = 0 + j = 0 + rc_index = (i * 256 // NUM_LED) + j + self.np[i] = self.wheel(rc_index & 255) + i = (i + 1) + if i == NUM_LED: + i = 0 + self.np.write() + j = (j + 1) % 255 + # Strobe + elif MODES[self.mode] == "strobe": + if not self.mode == old_mode: + old_mode = self.mode + j = 0 + if j == 1: + self.set_color(self.color) + else: + self.set_color(-1) + j = (j + 1) % 2 + time.sleep(DELAY) + else: + print("else") + + +def main(): + print("") + global light + light = Light() + light.start() + + +if __name__ == "__main__": + main() \ No newline at end of file