DotStar LED Strips

dotstar/__init__.py

from .led_strip import *

dotstar/led_strip.py

import machine

class LED_Strip():

    BLEND_MODE_OVERWRITE = 0
    BLEND_MODE_ADD = 1
    BLEND_MODE_MULTIPLY = 2

    def __init__(self, led_count, brightness = 1):
        self.led_count = led_count
        self.brightness = min(31, brightness) # 5-bit value (= 0 .. 31)

        # Create a nested array for use as off-screen frame buffer [^1]
        led_frame = [0b11100000 + brightness, 0, 0, 0]
        self.leds = [led_frame] * self.led_count

        # Setup hardware SPI connection (19 = MOSI, 18 = SCLK)
        self.spi = machine.SPI(0, baudrate=1000000, sck=machine.Pin(18), mosi=machine.Pin(19)) # Bus 0, clock speed 1 MHz

        self.clear()
        self.display()
        
    def set_color(self, rgb = 0, led_ids = None, blend_mode = BLEND_MODE_OVERWRITE):

        if type(led_ids) == int:  # Single led
            led_ids = range(led_ids, led_ids + 1)
        elif type(led_ids) == list or type(led_ids) == tuple or type(led_ids) == range:  # Range of leds
            pass
        else:  # All leds
            led_ids = range(self.led_count)

        if type(rgb) == int:
            red, green, blue = rgb >> 16, rgb >> 8 & 0xff, rgb & 0xff
        elif type(rgb) == list or type(rgb) == tuple:
            red, green, blue = rgb

        for led_id in led_ids:
            if blend_mode == self.BLEND_MODE_OVERWRITE:
                self.leds[led_id] = [0b11100000 + self.brightness, blue, green, red]
            elif blend_mode == self.BLEND_MODE_ADD:
                self.leds[led_id] = [max(0, min(255, color_1_component + color_2_component)) 
                    for color_1_component, color_2_component in zip(self.leds[led_id], [0, blue, green, red])]
            elif blend_mode == self.BLEND_MODE_MULTIPLY:
                self.leds[led_id] = [max(0, min(255, int(color_1_component * color_2_component / 255)))
                    for color_1_component, color_2_component in zip(self.leds[led_id], [255, blue, green, red])]

    def display(self):
        
        def flatten_list(iterables):
            for iterable in iterables:
                for item in iterable:
                    yield item
        
        start_frame = [0b00000000, 0b00000000, 0b00000000, 0b00000000] # Start frame: 4 Bytes of 0's
        led_frames  = list(flatten_list(self.leds))  # Flatten nested list (= list containing lists)
        end_frame   = [0x11111111] * ((self.led_count + 15) // 16)  # End frame: 1 bit for every two LEDs, rounded up to bytes 
        
        self.spi.write(bytearray(start_frame + led_frames + end_frame))
    
    def clear(self):
        self.set_color()  # Turn all LEDs in off-screen frame buffer off

    def cleanup(self):
        self.set_color()  # Turn all LEDs in off-screen frame buffer off
        self.display()  # Write off-screen frame buffer to LED strip
        self.spi.deinit()

77-dotstar_rainbow_sweep_w_afterglow.py

import dotstar

import time

led_count = 8
led_strip = dotstar.LED_Strip(led_count)

# From https://github.com/python/cpython/blob/main/Lib/colorsys.py
def hsv_to_rgb(h, s, v):
    if s == 0.0:
        return v, v, v
    i = int(h*6.0) # XXX assume int() truncates!
    f = (h*6.0) - i
    p = v*(1.0 - s)
    q = v*(1.0 - s*f)
    t = v*(1.0 - s*(1.0-f))
    i = i%6
    if i == 0:
        return v, t, p
    if i == 1:
        return q, v, p
    if i == 2:
        return p, v, t
    if i == 3:
        return p, q, v
    if i == 4:
        return t, p, v
    if i == 5:
        return v, p, q


try:

	step_count = 5  # The number of steps after color * step_size = 0 [^1]
	step_size = int(255 * (1 / 255) ** (1 / step_count))  # The resulting step_size
	
	hue = 0

	while True:
	
		for led_id in range(led_count - 1):
			r, g, b = hsv_to_rgb(hue / 360.0, 1, 1)
			led_strip.set_color((step_size, step_size, step_size), blend_mode = led_strip.BLEND_MODE_MULTIPLY)
			led_strip.set_color((int(255 * r), int(255 * g), int(255 * b)), led_id)
			led_strip.display()
			time.sleep(.05)
			hue += 5

		for led_id in range(led_count - 1, 0, -1):
			r, g, b = hsv_to_rgb(hue / 360.0, 1, 1)
			led_strip.set_color((step_size, step_size, step_size), blend_mode = led_strip.BLEND_MODE_MULTIPLY)
			led_strip.set_color((int(255 * r), int(255 * g), int(255 * b)), led_id)
			led_strip.display()
			time.sleep(.05)
			hue += 5

except KeyboardInterrupt:  # Control-C was pressed
	pass

led_strip.cleanup()