Compare commits

...

5 Commits

14 changed files with 141 additions and 22 deletions

8
.gitignore vendored
View File

@ -6,6 +6,8 @@
# General
/_tmp
/.upload_cache
*_bak
*_bak[0-9]
# Python
__pycache__
@ -18,8 +20,12 @@ __pycache__
*.mpy
# Configuration files
/Makefile.env
/src/lightbar_cfg.py
/src/webrepl_cfg.py
/src/wlan_cfg.py
# FreeCAD backups
# 3D modeling temporary files (FreeCAD backups, Cura projects, G-code files)
*.FCStd1
*.3mf
*.gcode

View File

@ -1,16 +1,19 @@
# Configuration
LIGHTBAR_HOST ?= 192.168.17.22
LIGHTBAR_URL := http://$(LIGHTBAR_HOST)
WEBREPL_CLI := webrepl_cli.py
WEBREPL_HOST := $(LIGHTBAR_HOST)
WEBREPL_PASSWORD ?= acab
REPL_TTY_PATH ?= /dev/ttyUSB0
# Configuration defaults (please use Makefile.env to override these, see Makefile.env.example)
LIGHTBAR_HOST = 192.168.13.12
LIGHTBAR_URL = http://$(LIGHTBAR_HOST)
WEBREPL_CLI = webrepl_cli.py
WEBREPL_HOST = $(LIGHTBAR_HOST)
WEBREPL_PASSWORD = ultra-secret-webrepl-password
REPL_TTY_PATH = /dev/ttyUSB0
# Include local config
-include Makefile.env
# Directory for saving timestamps of when files were last uploaded
UPLOAD_CACHE_DIR := .upload_cache
# Auto-detect all .py files that should be uploaded
SRC_UPLOAD_TARGETS := $(patsubst %.py,$(UPLOAD_CACHE_DIR)/%.py.timestamp,$(wildcard src/*.py src/**/*.py))
SRC_UPLOAD_TARGETS := $(patsubst %.py,$(UPLOAD_CACHE_DIR)/%.py.timestamp,$(wildcard src/*.py src/**/*.py src/**/**/*.py))
LIB_UPLOAD_TARGETS := $(patsubst %.py,$(UPLOAD_CACHE_DIR)/%.py.timestamp,$(wildcard lib/*.py))
# Default target
@ -52,7 +55,7 @@ $(UPLOAD_CACHE_DIR)/%.py.timestamp :: %.py
repl-create-directories:
@echo "(Note: If the directory already exists, rshell will print \"Unable to create [DIR]\")"
@echo
rshell -p $(REPL_TTY_PATH) mkdir /pyboard/lib /pyboard/lightbar
rshell -p $(REPL_TTY_PATH) mkdir /pyboard/lib /pyboard/lightbar /pyboard/lightbar/endpoints
# Clear the .upload_cache directory that contains the last upload timestamps
.PHONY: clear-upload-cache clear-upload-cache-src

11
Makefile.env.example Normal file
View File

@ -0,0 +1,11 @@
# This is an example file.
# Copy this to Makefile.env and adjust it to the IP and password of your lightbar.
# IP address or hostname of your lightbar
LIGHTBAR_HOST := 192.168.13.12
# Password for WebREPL as set in webrepl_cfg.py (used to transfer files to the ESP32)
WEBREPL_PASSWORD := ultra-secret-webrepl-password
# Device path to the serial port of the ESP32 when connected via USB
REPL_TTY_PATH := /dev/ttyUSB0

10
examples/lightbar_cfg.py Normal file
View File

@ -0,0 +1,10 @@
# Example file for Lightbar configuration. Copy to src/lightbar_cfg.py, adjust and upload to the board.
# Port to start the HTTP server on
SERVER_PORT = 80
# Whether the HTTP server should be started in debug mode
SERVER_DEBUG = False
# Amount of LEDs in the lightbar
NEOPIXEL_COUNT = 28

Binary file not shown.

Binary file not shown.

6
misc/chip_pinout.txt Normal file
View File

@ -0,0 +1,6 @@
Board: ESP32 NodeMCU
Pins:
- Neopixels: GPIO26
- Status LED: GPIO4
- Button: GPIO21 (against GND)

View File

@ -1,10 +1,12 @@
# boot.py -- This file is executed on every boot (including wake-boot from deepsleep)
from lightbar import wlan_manager
import uasyncio
import webrepl
# Connect to WLAN as defined in wlan_cfg.py
wlan_manager.connect()
from lightbar import wlan_manager
# Asynchronously connect to WLAN as defined in wlan_cfg.py
uasyncio.create_task(wlan_manager.connect_async())
# Start WebREPL on default port 8266 with password defined in webrepl_cfg.PASS
webrepl.start()

View File

@ -0,0 +1,43 @@
import uasyncio
from machine import Pin
from neopixel import NeoPixel
# Constants
NEOPIXEL_PIN = 26
class LightbarController:
"""
Controls the LEDs using the NeoPixel library.
"""
neopixel_count: int
neopixel: NeoPixel
def __init__(self, *, neopixel_count: int):
print('[LightbarController] Initializing Neopixels')
self.neopixel_count = neopixel_count
self.neopixel = NeoPixel(Pin(NEOPIXEL_PIN), neopixel_count)
def start(self):
uasyncio.create_task(self.run_loop())
async def run_loop(self):
print('[LightbarController] Turning on Neopixels')
np = self.neopixel
np.fill((0, 0, 0))
color_set = [
(255, 0, 0),
(255, 255, 0),
(0, 255, 0),
(0, 255, 255),
(0, 0, 255),
(255, 0, 255),
]
while True:
for i in range(self.neopixel_count):
np[i] = color_set[i % len(color_set)]
np.write()
await uasyncio.sleep(0.5)

View File

@ -2,11 +2,14 @@ import machine
import uasyncio
from microdot_asyncio import Microdot
from lightbar.frontend import frontend
from lightbar.rest_api import rest_api
from lightbar.endpoints.frontend import frontend
from lightbar.endpoints.rest_api import rest_api
class Lightbar(Microdot):
class LightbarServer(Microdot):
"""
Runs the HTTP server to control the lightbar.
"""
def __init__(self):
super().__init__()
self.mount(frontend)

View File

@ -1,6 +1,8 @@
import network
import time
import network
from machine import Pin
def connect() -> network.WLAN:
"""
@ -19,13 +21,21 @@ def connect() -> network.WLAN:
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
status_led = Pin(4, Pin.OUT)
status_led.off()
if not wlan.isconnected():
wlan.connect(wlan_cfg.SSID, wlan_cfg.PASSWORD)
print('[wlan_manager] Connecting to WLAN "{}" '.format(wlan_cfg.SSID), end='')
while not wlan.isconnected():
print('.', end='')
time.sleep(1)
# Let the LED blink to indicate the connection attempt
status_led.on()
time.sleep(0.5)
status_led.off()
time.sleep(0.5)
print('\n[wlan_manager] Connected!')
else:
print('[wlan_manager] Already connected to WLAN "{}"'.format(wlan.config('essid')))
@ -35,4 +45,16 @@ def connect() -> network.WLAN:
wlan.ifconfig(wlan_cfg.IFCONFIG)
print('[wlan_manager] IP config: {}'.format(wlan.ifconfig()))
# Let the LED blink 2 times quickly to indicate that the WLAN is connected
for _ in range(2):
time.sleep(0.125)
status_led.on()
time.sleep(0.125)
status_led.off()
return wlan
async def connect_async():
connect()

View File

@ -1,5 +1,18 @@
from lightbar.app import Lightbar
from lightbar.lightbar_controller import LightbarController
from lightbar.lightbar_server import LightbarServer
print('[main] Starting lightbar HTTP server...')
api_server = Lightbar()
api_server.run(port=80, debug=True)
try:
import lightbar_cfg
except Exception as e:
print('[main] Could not import lightbar_cfg.py: {}'.format(str(e)))
raise e
print('[main] Starting lightbar controller')
lightbar_controller = LightbarController(neopixel_count=lightbar_cfg.NEOPIXEL_COUNT)
lightbar_controller.start()
print('[main] Starting lightbar HTTP server')
api_server = LightbarServer()
api_server.run(port=lightbar_cfg.SERVER_PORT, debug=lightbar_cfg.SERVER_DEBUG)
print('[main] HTTP server shut down! End of main')