neopixel.py
1 """BCM283x NeoPixel Driver Class""" 2 import time 3 import atexit 4 import _rpi_ws281x as ws 5 6 # LED configuration. 7 # pylint: disable=redefined-outer-name,too-many-branches,too-many-statements 8 # pylint: disable=global-statement,protected-access 9 LED_CHANNEL = 0 10 LED_FREQ_HZ = 800000 # Frequency of the LED signal. We only support 800KHz 11 LED_DMA_NUM = 10 # DMA channel to use, can be 0-14. 12 LED_BRIGHTNESS = 255 # We manage the brightness in the neopixel library 13 LED_INVERT = 0 # We don't support inverted logic 14 LED_STRIP = None # We manage the color order within the neopixel library 15 16 # a 'static' object that we will use to manage our PWM DMA channel 17 # we only support one LED strip per raspi 18 _led_strip = None 19 _buf = None 20 21 22 def neopixel_write(gpio, buf): 23 """NeoPixel Writing Function""" 24 global _led_strip # we'll have one strip we init if its not at first 25 global _buf # we save a reference to the buf, and if it changes we will cleanup and re-init. 26 27 if _led_strip is None or buf is not _buf: 28 # This is safe to call since it doesn't do anything if _led_strip is None 29 neopixel_cleanup() 30 31 # Create a ws2811_t structure from the LED configuration. 32 # Note that this structure will be created on the heap so you 33 # need to be careful that you delete its memory by calling 34 # delete_ws2811_t when it's not needed. 35 _led_strip = ws.new_ws2811_t() 36 _buf = buf 37 38 # Initialize all channels to off 39 for channum in range(2): 40 channel = ws.ws2811_channel_get(_led_strip, channum) 41 ws.ws2811_channel_t_count_set(channel, 0) 42 ws.ws2811_channel_t_gpionum_set(channel, 0) 43 ws.ws2811_channel_t_invert_set(channel, 0) 44 ws.ws2811_channel_t_brightness_set(channel, 0) 45 46 channel = ws.ws2811_channel_get(_led_strip, LED_CHANNEL) 47 48 # Initialize the channel in use 49 count = 0 50 if len(buf) % 3 == 0: 51 # most common, divisible by 3 is likely RGB 52 LED_STRIP = ws.WS2811_STRIP_RGB 53 count = len(buf) // 3 54 elif len(buf) % 4 == 0: 55 LED_STRIP = ws.SK6812_STRIP_RGBW 56 count = len(buf) // 4 57 else: 58 raise RuntimeError("We only support 3 or 4 bytes-per-pixel") 59 60 ws.ws2811_channel_t_count_set( 61 channel, count 62 ) # we manage 4 vs 3 bytes in the library 63 ws.ws2811_channel_t_gpionum_set(channel, gpio._pin.id) 64 ws.ws2811_channel_t_invert_set(channel, LED_INVERT) 65 ws.ws2811_channel_t_brightness_set(channel, LED_BRIGHTNESS) 66 ws.ws2811_channel_t_strip_type_set(channel, LED_STRIP) 67 68 # Initialize the controller 69 ws.ws2811_t_freq_set(_led_strip, LED_FREQ_HZ) 70 ws.ws2811_t_dmanum_set(_led_strip, LED_DMA_NUM) 71 72 resp = ws.ws2811_init(_led_strip) 73 if resp != ws.WS2811_SUCCESS: 74 if resp == -5: 75 raise RuntimeError( 76 "NeoPixel support requires running with sudo, please try again!" 77 ) 78 message = ws.ws2811_get_return_t_str(resp) 79 raise RuntimeError( 80 "ws2811_init failed with code {0} ({1})".format(resp, message) 81 ) 82 atexit.register(neopixel_cleanup) 83 84 channel = ws.ws2811_channel_get(_led_strip, LED_CHANNEL) 85 if gpio._pin.id != ws.ws2811_channel_t_gpionum_get(channel): 86 raise RuntimeError("Raspberry Pi neopixel support is for one strip only!") 87 88 if ws.ws2811_channel_t_strip_type_get(channel) == ws.WS2811_STRIP_RGB: 89 bpp = 3 90 else: 91 bpp = 4 92 # assign all colors! 93 for i in range(len(buf) // bpp): 94 r = buf[bpp * i] 95 g = buf[bpp * i + 1] 96 b = buf[bpp * i + 2] 97 if bpp == 3: 98 pixel = (r << 16) | (g << 8) | b 99 else: 100 w = buf[bpp * i + 3] 101 pixel = (w << 24) | (r << 16) | (g << 8) | b 102 ws.ws2811_led_set(channel, i, pixel) 103 104 resp = ws.ws2811_render(_led_strip) 105 if resp != ws.WS2811_SUCCESS: 106 message = ws.ws2811_get_return_t_str(resp) 107 raise RuntimeError( 108 "ws2811_render failed with code {0} ({1})".format(resp, message) 109 ) 110 time.sleep(0.001 * ((len(buf) // 100) + 1)) # about 1ms per 100 bytes 111 112 113 def neopixel_cleanup(): 114 """Cleanup when we're done""" 115 global _led_strip 116 117 if _led_strip is not None: 118 # Ensure ws2811_fini is called before the program quits. 119 ws.ws2811_fini(_led_strip) 120 # Example of calling delete function to clean up structure memory. Isn't 121 # strictly necessary at the end of the program execution here, but is good practice. 122 ws.delete_ws2811_t(_led_strip) 123 _led_strip = None