Bugünkü yazımızda Raspberry Pi Pico ile Neopixel ledlerin nasıl kullanıldığına değineceğiz ve örnek bir proje yapacağız.
Malzeme Listesi
- Raspberry Pi Pico
- Neopixel Led
- Jumper Kablo
Neopixel Led Nedir?
Neopixel ya da diğer adıyla adreslenebilir ledlerin en büyük özelliği ledleri tek tek adreslenerek çalıştırılabilmesidir. Örneğin normal bir şerit lede direkt güç verdiğinizde büyük ledler aynı parlaklıkta ve aynı renkte yanar. Fakat neopixel şerit ledler her bir ledin parlaklığını, rengini spesifik olarak belirleme imkanı sunuyor.
Piyasada boyutlarına ve şekillerine göre ayrılmış, halka, şerit, çubuk ve tekli birçok model bulunmaktadır. Basit projelerde en çok WS2812B tercih edilmektedir. Aşağıda halka neopixel ledin resmini görebilirsiniz.
Neopixel ledlerin ne olduğunu öğrendiğimize göre projemizin kodlarına ve devre şemasına geçebiliriz.
Raspberry Pi Pico ile Neopixel Led – Devre Şeması
Devre şemamız oldukça basit. Tek yapmanız gereken ledin VCC pinini Pico’nun 3.3V pinine, GND pinini GND hattına, D (Data) pinini ise 1 numaralı GPIO0 pinine bağlamak.
Eğer sizin de lediniz 4 çıkışlı ise DI (data input) pini Pico’nun GPIO0 pinine gitmelidir. DO (data out) pini ledleri birbirine bağlamak için kullanılır.
Şimdi ledleri sürmek için kodlara ama kütüphaneye göz atabiliriz.
Raspberry Pi Pico ile Neopixel Led – Kodlar
Aşağıdaki kod parçasını neopixel.py adıyla Pico üzerine kaydedelim. Bu kodlar ledleri daha rahat kullanmak için gerekli kütüphane yapısını oluşturuyor.
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 supports different order of individual colors (GRB, RGB, WRGB, GWRB …). In order to achieve | |
# this, we need to flip the indexes: in 'RGBW', 'R' is on index 0, but we need to shift it left by 3 * 8bits, | |
# so in it's inverse, 'WBGR', it has exactly right index. Since micropython doesn't have [::-1] and recursive rev() | |
# isn't too efficient we simply do that by XORing (operator ^) each index with 3 (0b11) to make this flip. | |
# When dealing with just 'RGB' (3 letter string), this means same but reduced by 1 after XOR!. | |
# Example: in 'GRBW' we want final form of 0bGGRRBBWW, meaning G with index 0 needs to be shifted 3 * 8bit -> | |
# 'G' on index 0: 0b00 ^ 0b11 -> 0b11 (3), just as we wanted. | |
# Same hold for every other index (and – 1 at the end for 3 letter strings). | |
class Neopixel: | |
def __init__(self, num_leds, state_machine, pin, mode="RGB", delay=0.0001): | |
self.pixels = array.array("I", [0 for _ in range(num_leds)]) | |
self.mode = set(mode) # set for better performance | |
if 'W' in self.mode: | |
# RGBW uses different PIO state machine configuration | |
self.sm = rp2.StateMachine(state_machine, sk6812, freq=8000000, sideset_base=Pin(pin)) | |
# dictionary of values required to shift bit into position (check class desc.) | |
self.shift = {'R': (mode.index('R') ^ 3) * 8, 'G': (mode.index('G') ^ 3) * 8, | |
'B': (mode.index('B') ^ 3) * 8, 'W': (mode.index('W') ^ 3) * 8} | |
else: | |
self.sm = rp2.StateMachine(state_machine, ws2812, freq=8000000, sideset_base=Pin(pin)) | |
self.shift = {'R': ((mode.index('R') ^ 3) – 1) * 8, 'G': ((mode.index('G') ^ 3) – 1) * 8, | |
'B': ((mode.index('B') ^ 3) – 1) * 8, 'W': 0} | |
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, how_bright = None): | |
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), how_bright) | |
else: | |
self.set_pixel(left_pixel + i, (red, green, blue), how_bright) | |
# 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, how_bright = None): | |
for i in range(pixel1, pixel2 + 1): | |
self.set_pixel(i, rgb_w, how_bright) | |
# Set red, green and blue value of pixel on position <pixel_num> | |
# Function accepts (r, g, b) / (r, g, b, w) tuple | |
def set_pixel(self, pixel_num, rgb_w, how_bright = None): | |
if how_bright == None: | |
how_bright = self.brightness() | |
pos = self.shift | |
red = round(rgb_w[0] * (how_bright / 255)) | |
green = round(rgb_w[1] * (how_bright / 255)) | |
blue = round(rgb_w[2] * (how_bright / 255)) | |
white = 0 | |
# if it's (r, g, b, w) | |
if len(rgb_w) == 4 and 'W' in self.mode: | |
white = round(rgb_w[3] * (how_bright / 255)) | |
self.pixels[pixel_num] = white << pos['W'] | blue << pos['B'] | red << pos['R'] | green << pos['G'] | |
# Converts HSV color to rgb tuple and returns it | |
# Function accepts integer values for <hue>, <saturation> and <value> | |
# The logic is almost the same as in Adafruit NeoPixel library: | |
# https://github.com/adafruit/Adafruit_NeoPixel so all the credits for that | |
# go directly to them (license: https://github.com/adafruit/Adafruit_NeoPixel/blob/master/COPYING) | |
def colorHSV(self, hue, sat, val): | |
if hue >= 65536: | |
hue %= 65536 | |
hue = (hue * 1530 + 32768) // 65536 | |
if hue < 510: | |
b = 0 | |
if hue < 255: | |
r = 255 | |
g = hue | |
else: | |
r = 510 – hue | |
g = 255 | |
elif hue < 1020: | |
r = 0 | |
if hue < 765: | |
g = 255 | |
b = hue – 510 | |
else: | |
g = 1020 – hue | |
b = 255 | |
elif hue < 1530: | |
g = 0 | |
if hue < 1275: | |
r = hue – 1020 | |
b = 255 | |
else: | |
r = 255 | |
b = 1530 – hue | |
else: | |
r = 255 | |
g = 0 | |
b = 0 | |
v1 = 1 + val | |
s1 = 1 + sat | |
s2 = 255 – sat | |
r = ((((r * s1) >> 8) + s2) * v1) >> 8 | |
g = ((((g * s1) >> 8) + s2) * v1) >> 8 | |
b = ((((b * s1) >> 8) + s2) * v1) >> 8 | |
return r, g, b | |
# Rotate <num_of_pixels> 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 <num_of_pixels> 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 mode is 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, how_bright = None): | |
for i in range(self.num_leds): | |
self.set_pixel(i, rgb_w, how_bright) | |
time.sleep(self.delay) |
Aşağıdaki kodu ise main.py adıyla Pico üzerine kaydedelim. Eğer başka isimle kaydederseniz Pico’yu bilgisayardan ayırıp harici bir güç kaynağıyla beslediğinizde kodun otomatik olarak çalışmayacağını unutmayın.
from neopixel import Neopixel | |
import time | |
numpix = 12 #number of neopixel leds | |
strip = Neopixel(numpix, 0, 0, "GRB") | |
color = (0, 255, 255) #specify colour as RGB | |
strip.brightness(25) #brigtness of neopixels led | |
for i in range(numpix): | |
strip.set_pixel(i, color) #setpixel(lednumber, colour) | |
strip.show() #show | |
time.sleep(sleep_time) |
Sonuç
4. satırda ledlerinizin sayısını doğru belirtmeyi unutmayın ve 8. satırda da ledin rengini RGB renk skalasına göre özelleştirebilirsiniz, sonuç olarak kurulumu başarıyla tamamladıysanız, main.py kodunu çalıştırdığınızda ledlerin sırayla yandığı bir animasyon görmelisiniz.
“Raspberry Pi Pico ile Neopixel Led Kullanımı” projemizin sonuna geldik. Bu proje hakkında herhangi bir sorunuz olursa bu gönderiye yorum olarak yazabilir veya mail adresimden sosyal medyadan bana ulaşabilirsiniz.
İyi çalışmalar dilerim.
Raspberry Pi Pico yazımızı okudunuz mu?