From 3b6bfaf8f8a597ec1cc98d0718d99ca6f43ff597 Mon Sep 17 00:00:00 2001 From: blaz-r Date: Mon, 12 Apr 2021 14:24:35 +0200 Subject: [PATCH] added library code --- neopixel.py | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 neopixel.py diff --git a/neopixel.py b/neopixel.py new file mode 100644 index 0000000..a0de228 --- /dev/null +++ b/neopixel.py @@ -0,0 +1,135 @@ +import array, time +from machine import Pin +import rp2 + +# PIO state machine for RGB. Pulls 24 bits (rgb -> 3 * 8bit) automatically +@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24) +def ws2812(): + T1 = 2 + T2 = 5 + T3 = 3 + wrap_target() + label("bitloop") + out(x, 1) .side(0) [T3 - 1] + jmp(not_x, "do_zero") .side(1) [T1 - 1] + jmp("bitloop") .side(1) [T2 - 1] + label("do_zero") + nop().side(0) [T2 - 1] + wrap() + +# PIO state machine for RGBW. Pulls 32 bits (rgbw -> 4 * 8bit) automatically +@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=32) +def sk6812(): + T1 = 2 + T2 = 5 + T3 = 3 + wrap_target() + label("bitloop") + out(x, 1) .side(0) [T3 - 1] + jmp(not_x, "do_zero") .side(1) [T1 - 1] + jmp("bitloop") .side(1) [T2 - 1] + label("do_zero") + nop() .side(0) [T2 - 1] + wrap() + + +# delay here is the reset time. You need a pause to reset the LED strip back to the initial LED +# however, if you have quite a bit of processing to do before the next time you update the strip +# you could put in delay=0 (or a lower delay) +class neopixel: + def __init__(self, num_leds, state_machine, pin, mode="RGB", delay=0.001): + self.pixels = array.array("I", [0 for _ in range(num_leds)]) + self.mode = set(mode) + # RGBW uses different PIO state machine configuration + if 'W' in self.mode: + self.sm = rp2.StateMachine(state_machine, sk6812, freq=8000000, sideset_base=Pin(pin)) + else: + self.sm = rp2.StateMachine(state_machine, ws2812, freq=8000000, sideset_base=Pin(pin)) + self.sm.active(1) + self.num_leds = num_leds + self.delay = delay + self.brightnessvalue = 255 + + # Set the overal value to adjust brightness when updating leds + def brightness(self, brightness=None): + if brightness == None: + return self.brightnessvalue + else: + if (brightness < 1): + brightness = 1 + if (brightness > 255): + brightness = 255 + self.brightnessvalue = brightness + + # Create a gradient with two RGB colors between "pixel1" and "pixel2" (inclusive) + # Function accepts two (r, g, b) / (r, g, b, w) tuples + def set_pixel_line_gradient(self, pixel1, pixel2, left_rgb_w, right_rgb_w): + if pixel2 - pixel1 == 0: + return + right_pixel = max(pixel1, pixel2) + left_pixel = min(pixel1, pixel2) + + for i in range(right_pixel - left_pixel + 1): + fraction = i / (right_pixel - left_pixel) + red = round((right_rgb_w[0] - left_rgb_w[0]) * fraction + left_rgb_w[0]) + green = round((right_rgb_w[1] - left_rgb_w[1]) * fraction + left_rgb_w[1]) + blue = round((right_rgb_w[2] - left_rgb_w[2]) * fraction + left_rgb_w[2]) + # if it's (r, g, b, w) + if len(left_rgb_w) == 4 and 'W' in self.mode: + white = round((right_rgb_w[3] - left_rgb_w[3]) * fraction + left_rgb_w[3]) + self.set_pixel(left_pixel + i, (red, green, blue, white)) + else: + self.set_pixel(left_pixel + i, (red, green, blue)) + + # Set an array of pixels starting from "pixel1" to "pixel2" (inclusive) to the desired color. + # Function accepts (r, g, b) / (r, g, b, w) tuple + def set_pixel_line(self, pixel1, pixel2, rgb_w): + for i in range(pixel1, pixel2 + 1): + self.set_pixel(i, rgb_w) + + # Set red, green and blue value of pixel on position + # Function accepts (r, g, b) tuple or individual rgb values3 + def set_pixel(self, pixel_num, rgb_w): + + red = round(rgb_w[0] * (self.brightness() / 255)) + green = round(rgb_w[1] * (self.brightness() / 255)) + blue = round(rgb_w[2] * (self.brightness() / 255)) + # if it's (r, g, b, w) + if len(rgb_w) == 4 and 'W' in self.mode: + white = round(rgb_w[3] * (self.brightness() / 255)) + # bits are of form 0bGGRRBBWW + self.pixels[pixel_num] = green << 24 | red << 16 | blue << 8 | white + else: + self.pixels[pixel_num] = green << 16 | red << 8 | blue + + # Rotate pixels to the left + def rotate_left(self, num_of_pixels): + if num_of_pixels == None: + num_of_pixels = 1 + self.pixels = self.pixels[num_of_pixels:] + self.pixels[:num_of_pixels] + + # Rotate pixels to the right + def rotate_right(self, num_of_pixels): + if num_of_pixels == None: + num_of_pixels = 1 + num_of_pixels = -1 * num_of_pixels + self.pixels = self.pixels[num_of_pixels:] + self.pixels[:num_of_pixels] + + # Update pixels + def show(self): + # if we use only RGB, we cut 8 bits of, otherwise we keep all 32 + cut = 8 + if 'W' in self.mode: + cut = 0 + for i in range(self.num_leds): + self.sm.put(self.pixels[i], cut) + time.sleep(self.delay) + + # Set all pixels to given rgb values + # Function accepts (r, g, b) / (r, g, b, w) + def fill(self, rgb_w): + for i in range(self.num_leds): + self.set_pixel(i, rgb_w) + time.sleep(self.delay) + +