Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved WS2812B support, fixed library import from 2 different trees, added demo to control neopixels with on/off and brightness control from tux touch pads #32

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
4 changes: 4 additions & 0 deletions configuration/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

# allow for quick repeated push of files after you modify them.
../examples/install.sh "$@"
8 changes: 5 additions & 3 deletions configuration/led.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
settings = { # LED panel or string
"apa106": False, # IoTuz and LoliBot use APA106 RGB LEDs
# "dimension": (1,),
"dimension": (8,), # Edge lit acrylic panel
# "dimension": (8,), # Edge lit acrylic panel
"dimension": (46,),
# "dimension": (8, 8),
# "dimension": (32, 8),
# "dimension": (32, 32),
# "neopixel_pin": 4, # TinyPICO
"neopixel_pin": 13, # Usually
"neopixel_pin": 19, # swagbadge SAO_1 bottom right viewed from front
# "neopixel_pin": 15, # Wemos OLED
# "neopixel_pin": 23, # IoTuz
"zigzag": False # For 2D panels
"zigzag": False, # For 2D panels
"dim": 0.2, # default dim to save batteries/power over USB
}
168 changes: 168 additions & 0 deletions examples/badge_np.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@

from aiko import led,oled
from machine import Pin, TouchPad
import aiko.event
import time
from threading import Thread

# from examples.badge_np import run; run()

# this is set by aiko/led.py and configured in configuration/led.py
#num_np = 46
#np = neopixel.NeoPixel(Pin(19), 46)

# Neopixel demo code (c) Adafruit, adapted to Aiko by Marc MERLIN
# https://learn.adafruit.com/circuitpython-essentials/circuitpython-neopixel
def wheel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if pos < 0 or pos > 255:
r = g = b = 0
elif pos < 85:
r = int(pos * 3)
g = int(255 - pos * 3)
b = 0
elif pos < 170:
pos -= 85
r = int(255 - pos * 3)
g = 0
b = int(pos * 3)
else:
pos -= 170
r = 0
g = int(pos * 3)
b = int(255 - pos * 3)
return (r, g, b)


def rainbow_cycle(wait, loopcnt):
for j in range(loopcnt):
for i in range(num_np):
pixel_index = (i * 256 // num_np) + j

led.pixel(wheel(pixel_index & 255), i)
led.write()
time.sleep(wait)

def color_chase(color, wait):
for i in range(num_np):
led.pixel(color, i, True)
time.sleep(wait)

BLACK = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 150, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
num_np = 0

badge_np_init = False

# left tux has a red LED with 50Ohm resistor between
# GPIO 19 (used for WS2812). This shows the control commands
# by blinking the red led.
# left tux also has one green LED plugged into GPIO22 and GND
leftTuxLedPin=Pin(22, Pin.OUT)

# right tux has 2 low power green eye LEDs plugged into SDA
# green is important as red/yellow is too low voltage and
# would take too many amps, as well as damage the I2C signal
# right tux has a front and back copper plane that can be
# used as a touchpad. Front is GPIO33 and rear is GPIO32

# On my board, with my soldering and my fingers, I see this:
# 1) when I'm not touching anything pads, read about 135
# 2) after being touched, they rebound to 5xx and quickly go
# back down to 135
# 3) touching the front nose gives about 50-80 depending on pressure
# 4) touching the front foot gives 20-50 depending on pressure and my finger
# 5) touching the back foot gives around 20-50 on tux_rear
tux_front = TouchPad(Pin(33))
tux_rear = TouchPad(Pin(32))


left_tux_led_state = True
def toggle_left_tux_led():
global left_tux_led_state
if tux_front.read() < 100 or tux_rear.read() < 100:
left_tux_led_state = True
else:
left_tux_led_state = not left_tux_led_state
leftTuxLedPin.value(left_tux_led_state)

last_title=""
title=""
np_on = True
def right_tux_touch():
global title, last_title, np_on
title=""
front_touch = tux_front.read() < 100
rear_touch = tux_rear.read() < 100
if front_touch and rear_touch:
title = "Front+Rear"
if np_on:
print("Both Front and rear tux touched, toggle LEDs off")
led.set_dim(0)
np_on = False
else:
print("Both Front and rear tux touched, toggle LEDs on")
led.reset_dim()
np_on = True
elif front_touch:
print("Front tux touched, brighten")
title = "Front Touch"
led.change_dim(0.1)
elif rear_touch:
print("Rear tux touched, darken")
title = "Rear Touch"
led.change_dim(-0.1)
if title != last_title:
last_title = title
print("Changing title to", title)
oled.set_title(title)
oled.oleds_show()
# Debug to make sure we get the correct dim value from MQTT
#print("tux_touch:"); led.print_dim()
#led.set_dim(0.4)


# Set configuration/main.py "application": "examples/badge_np"
def initialise():
global badge_np_init
print("Init badge_np")
# Does not work
oled.set_title("Init BadgeNP")
oled.oleds_show()
led.initialise()
aiko.event.add_timer_handler(toggle_left_tux_led, 300, immediate=False)
aiko.event.add_timer_handler(right_tux_touch, 500, immediate=False)
badge_np_init = True
# https://pymotw.com/2/threading/
# https://realpython.com/intro-to-python-threading/
# run never returns, so run it in its own thread
Thread(target=run, args=(True, )).start()

def run(thread=False):
if thread:
print("Start badge_np thread")
oled.set_title("")
if not badge_np_init: initialise()

global num_np
num_np = led.length

while True:
# Debug to make sure we get the correct dim value from MQTT
#print("loop1:"); led.print_dim()
color_chase(BLACK, 0.01)
color_chase(RED, 0.01)
color_chase(YELLOW, 0.01)
color_chase(GREEN, 0.01)
color_chase(CYAN, 0.01)
color_chase(BLUE, 0.01)
color_chase(PURPLE, 0.01)

rainbow_cycle(0.005, 200) # rainbow cycle with 1ms delay per step

44 changes: 37 additions & 7 deletions lib/aiko/led.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
#
# Usage
# ~~~~~
# import aiko.led
# aiko.led.initialise()
# aiko.led.pixel(aiko.led.red, 0, True)
# from aiko import led
# led.initialise()
# led.pixel(aiko.led.red, 0, True)
#
# MQTT commands
# ~~~~~~~~~~~~~
Expand Down Expand Up @@ -34,9 +34,11 @@
from neopixel import NeoPixel

import configuration.led
import configuration.main

import urandom
apa106 = False
# this is overrriden by value read from settings at init time
dim = 0.1 # 100% = 1.0
full = 255
length = None
Expand All @@ -62,7 +64,6 @@
yellow = colors["yellow"]

def apply_dim(color, dimmer=None):
global dim
marcmerlin marked this conversation as resolved.
Show resolved Hide resolved
if dimmer == None: dimmer = dim
red = int(color[0] * dimmer)
green = int(color[1] * dimmer)
Expand All @@ -71,8 +72,32 @@ def apply_dim(color, dimmer=None):
# TODO: Now fails when called by exec(), see lib/aiko/mqtt.py
# return tuple([int(element * dimmer) for element in color])

def fill(color):
# Allow setting dim from code or MQTT
def set_dim(dimmer):
global dim
dim = max(min(dimmer, 1), 0)

# this is used to turn the neopixels back on to default brightness
def reset_dim():
parameter = configuration.main.parameter
set_dim(parameter("dim", configuration.led.settings))

# Take from -0.9 to 0.9 (+-0.1 is more typical) and adjust diming
# value. Make sure it stays within 0 to 1
def change_dim(change):
set_dim(dim + change)

def print_dim():
print("Dim: ", dim)

def fill(color, write=True):
np.fill(apply_dim((color[0], color[1], color[2])))
if write: np.write()
marcmerlin marked this conversation as resolved.
Show resolved Hide resolved

# write is needed if you use fill() or lots of pixel writes
# and then you decide to push the result.
def write():
np.write()

# Bresenham's line algorithm
def line(color, x0, y0, x1, y1):
Expand Down Expand Up @@ -143,6 +168,7 @@ def initialise(settings=configuration.led.settings):
parameter = configuration.main.parameter
apa106 = parameter("apa106", settings)
zigzag = parameter("zigzag", settings)
reset_dim()

length = linear(settings["dimension"])
length_x = settings["dimension"][0]
Expand All @@ -158,11 +184,15 @@ def on_led_message(topic, payload_in):
return True

if payload_in.startswith("(led:dim "):
global dim
tokens = [float(token) for token in payload_in[9:-1].split()]
dim = tokens[0]
set_dim(tokens[0])
return True

# When I send one debug message, this gets printed twice. Why?
if payload_in == "(led:debug)":
print_dim()
marcmerlin marked this conversation as resolved.
Show resolved Hide resolved
print("length: ", length)

if payload_in.startswith("(led:fill "):
tokens = [int(token) for token in payload_in[10:-1].split()]
color = (tokens[0], tokens[1], tokens[2])
Expand Down
8 changes: 4 additions & 4 deletions lib/aiko/oled.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
#
# Usage
# ~~~~~
# import aiko.oled
# aiko.oled.initialise()
# from aiko import oled
# oled.initialise()
# oled.set_title("Title")
# oled.write_title()
# oled.log("Log message"))
#
# image = aiko.oled.load_image("examples/tux_nice.pbm")
# oled0 = aiko.oled.oleds[0]
# image = oled.load_image("examples/tux_nice.pbm")
# oled0 = oled.oleds[0]
# oled0.fill(0)
# oled0.blit(image, 32, 0)
# oled0.show()
Expand Down