DotStar LED Strips

Download all scripts: 7-dotstar_led_strips.zip

DotStars & NeoPixels comparison

DotStars

NeoPixels

Interface

▽ 4-wire

▲ 3-wire

Timing

▲ flexible, standard SPI

▽ strict, custom

Data rate

▲ flexible up to 32 MHz

▽ fixed to 800 kHz

PWM rate

▲ 19.2 kHz

▽ 400 Hz

Price 1

▽ ~ 30 EUR/m

▲ ~ 20 EUR/m

LED colors

▽ RGB, W

▲ RGB, RGBW, W

LEDs

APA102, APA102C, SK9822

WS2812b, WS2813, SK6812, SK6822, APA104

Data per LED

4 Bytes

3 Bytes

Max. LEDs 2

160.000

5.333

1

For 1m of a 60 LEDs/m strip

2

Theoretical number (Data rate / Data per LED / 50 Hz)

References

APA Electronic co., Ltd APA102C
World Semi WS2812B

../../../../_images/7-dotstar_led_strips-OUTLINES.svg

dotstar/__init__.py

#!/usr/bin/env python3

from .led_strip import *

dotstar/led_strip.py

#!/usr/bin/env python3

import Adafruit_GPIO.SPI as SPI
from itertools import chain

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 = max(0, min(31, brightness)) # 5-bit value (= 0 .. 31)

		# Setup hardware SPI connection (10 = MOSI, 11 = SCLK)
		self.spi = SPI.SpiDev(1, 0, 8000000) # Bus 1, chip select 0, clock speed 8 MHz
	
		self.clear()  # Creates an array for use as off-screen frame buffer
		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):
		start_frame = [0b00000000, 0b00000000, 0b00000000, 0b00000000] # Start frame: 4 Bytes of 0's
		led_frames  = list(chain.from_iterable(self.leds))  # Flatten nested list
		end_frame   = [0x11111111] * ((self.led_count + 15) // 16)  # End frame: 1 bit for every two LEDs, rounded up to bytes
		frames = start_frame + led_frames + end_frame
		
		# spi.write is limited to 4096 bytes (= 1007 LEDs)
		chunks =  [frames[chunk_start : chunk_start + 4096] for chunk_start in range(0, len(frames), 4096)]		
		for chunk in chunks:
			self.spi.write(chunk)


	def clear(self):
		led_frame = [0b11100000 + self.brightness, 0, 0, 0]
		self.leds = [led_frame] * self.led_count


	def cleanup(self):
		self.clear()
		self.display()
		self.spi.close()

74-dotstar_sweep.py

#!/usr/bin/env python3

import dotstar

import time

led_count = 8
led_strip = dotstar.LED_Strip(10)

try:
	
	while True:
		for led_id in range(led_count - 1):  # Iterate from 0 to 8
			led_strip.set_color(0xff0000, led_id)
			led_strip.display()
			time.sleep(.05)
			led_strip.set_color(0x000000, led_id)
			led_strip.display()
			
		for led_id in range(led_count - 1, 0, -1):  # Iterate from 9 to 1
			led_strip.set_color(0xff0000, led_id)
			led_strip.display()
			time.sleep(.05)
			led_strip.set_color(0x000000, led_id)
			led_strip.display()
		
except KeyboardInterrupt:  # Control-C was pressed
	pass

led_strip.cleanup()
print('\nBye, bye.')

"""
DotStar LED strips make it easy to indvidually control a large amount of RGB LEDs. They require only a 4 wire connection to the first LED, all others are connected to each other.

The 4 connections are
	
	VCC	... +5V power supply
	GND ... Ground
	CI  ... Clock input
	DI  ... Data input

The LEDs can be individually addressed using an SPI (Serial Peripheral Interface) connection using the following protocol:

Start frame: 4 bytes of 0's

	start_frame = [0b00000000, 0b00000000, 0b00000000, 0b00000000]

LED frames: 4 bytes 

	led_frame = [0b11100000 + brightness, blue, red, green]

End frame: for every 2 LEDs an additional clock cycle has to be added. This can be done by sending additional data bytes.

	end_frame   = [0x11111111] * ((led_count + 31) // 16)

References
----------
APA 102C datasheet: http://neon-world.com/uploads/soft/20161130/1480487678.pdf

DotStars & NeoPixels: https://learn.adafruit.com/adafruit-dotstar-leds?view=all
"""

75-dotstar_color_cycle.py

#!/usr/bin/env python3

import dotstar

import time, colorsys

led_count = 8
led_strip = dotstar.LED_Strip(led_count)

try:

	led_ids = range(0, led_count, 2)  # Every second led
	
	cycle_duration = 2  # Seconds for a full color cycle
	resolution = 1  # Degrees resolution

	while True:
		
		for hue in range(0, 360, resolution):
			# Convert the hue value to the r, g, b values		
			r, g, b = colorsys.hsv_to_rgb(hue / 360.0, 1, 1)  # The range of the r, g, b values is 0.0 .. 1.0

			led_strip.set_color([int(255 * r), int(255 * g), int(255 * b)], led_ids)
			led_strip.display()

			time.sleep(cycle_duration / (360 / resolution))

		
except KeyboardInterrupt:  # Control-C was pressed
	pass

led_strip.cleanup()
print('\nBye, bye.')

"""
DotStar LED strips make it easy to indvidually control a large amount of RGB LEDs. They require only a 4 wire connection to the first LED, all others are connected to each other.

The 4 connections are
	
	VCC	... +5V power supply
	GND ... Ground
	CI  ... Clock input
	DI  ... Data input

The LEDs can be individually addressed using an SPI (Serial Peripheral Interface) connection using the following protocol:

Start frame: 4 bytes of 0's

	start_frame = [0b00000000, 0b00000000, 0b00000000, 0b00000000]

LED frames: 4 bytes 

	led_frame = [0b11100000 + brightness, blue, red, green]

End frame: for every 2 LEDs an additional clock cycle has to be added. This can be done by sending additional data bytes.

	end_frame   = [0x11111111] * ((led_count + 31) // 16)

References
----------
APA 102C datasheet: http://neon-world.com/uploads/soft/20161130/1480487678.pdf

DotStars & NeoPixels: https://learn.adafruit.com/adafruit-dotstar-leds?view=all
"""

77-dotstar_rainbow_sweep_w_afterglow.py

#!/usr/bin/env python3

import dotstar

import time, colorsys

led_count = 8
led_strip = dotstar.LED_Strip(led_count)

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 = colorsys.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 = colorsys.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()
print('\nBye, bye.')

"""
[^1]
Due to rounding errors the number of steps might be less

E.g. 

	step_count = 15 --> step_size = 176

    step | color 
    -----+------
       0 |  176
       1 |  121
       2 |   83
       3 |   57
       4 |   39
       5 |   26
       6 |   17
       7 |   11
       8 |    7
       9 |    4
      10 |    2
      11 |    1
      12 |    0
      13 |    0
      14 |    0

print('step_count = {} --> step_size = {}\n'.format(step_count, step_size))
print('step | color \n-----+------')
color = 255
for step in range(step_count):
	color = int(color * step_size / 255)
	print(' {:>3} |  {:>3}'.format(step, color))
"""