4 minute read

This documents Python example code for the 50mm 12 NeoPixel (WS2812B) Ring connected to a BBC micro:bit through the micro:bit Python Editor.

Assumptions

  • This example code uses the micro:bit Python NeoPixel library with initialization np = neopixel.NeoPixel(pin2, 12) to initialize $12$ NeoPixels connected to DI on pin2. The NeoPixel library works on pin0, pin1, or pin2. pin2 is the nearest clippable micro:bit pin to the 3V and GND clippable pins.
  • The 50mm 12 NeoPixel (WS2812B) Ring mounting holes are 2mm diameter on the corners of a 32mm square.
  • The 50mm 12 NeoPixel (WS2812B) Ring electrical connector has 4 solder-pad connections that are on 0.1” centers and are ≈2.54mm × ≈1.27mm. The pins are:
Pin Signal I/O Notes
1 DI I DI (input)
2 5V +V +5-7V
3 GND 0V
4 DO O DO (output to further NeoPixel strands)

  • NeoPixels can take RGB values on $[0, 255]$, but saturate at brightness levels $> 30$. Limiting each color to $30$ also limits the current needed to $\approx 12\%$ of the $60$ ma quoted as the maximum for a WS2812B NeoPixel (or $7$ ma).
  • To limit the RGB color values to $< 30$ the example code uses the compress function to compress values on $[0, 1)$ to generate integers up to a maximum value on an exponential scale such that smaller values cover a larger part of the range and larger values cover a smaller part of the range. This is shown on a Desmos graph.
  • The example code includes random_lights and chase_lights functions.
    • random_lights creates $12$ random colors with compressed RGB color values $< 30$ then uses the unbiased Fisher-Yates (aka Knuth) algorithm to shuffle them every delayms.
    • chase_lights creates $12$ colors scaled by $\frac{i}{12}$ for $i$ on $[\negthinspace[ 0, 12 )\negthinspace)$ with compressed RGB color values $< 30$ then rotates them every delayms.
  • The example event loop sets random NeoPixels and shuffles them every delay ms until button_a is pressed, then sets chasing NeoPixels every delay ms until button_a is pressed, then repeats.

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# Imports go at the top
from microbit import *
import math, neopixel, random

np = neopixel.NeoPixel(pin2, 12)
n, delay, maximum = 12, 200, 30

# https://stackoverflow.com/a/2450976/17467335
# The de-facto unbiased shuffle algorithm is the Fisher-Yates
# (aka Knuth) Shuffle.
# See https://github.com/coolaj86/knuth-shuffle.
def shuffle(array):
    """Return array shuffled in place with the unbiased Fisher-Yates
    (aka Knuth) algorithm."""
    current_index = len(array)

    # While there remain elements to shuffle...
    while 0 != current_index:

        # Pick a remaining element...
        random_index = int(random.random() * current_index)
        current_index -= 1

        # And swap it with the current element.
        array[current_index], array[random_index] = \
            array[random_index], array[current_index]

    return array

def compress(x, scale=1):
    """Return x on [0, 1) exponentially compressed, scaled to scale,
    and floored."""
    # https://www.desmos.com/calculator/gdcw7rndv2
    # The compression factor of 4 is good. It must be greater than e
    # for color[j] > 0.
    return int(math.exp(4 * (x - 1)) * scale)

def random_lights(n=12, maximum=30):
    """Return n random leds with maximum brightness (< 256)."""
    leds, color = [tuple(), ] * n, [0, ] * 3
    for i in range(len(leds)):
        for j in range(len(color)):
            color[j] = compress(random.random(), maximum)
        leds[i] = tuple(color)
    return leds

def chase_lights(rot, color=(102, 51, 153, ), n=12, maximum=30):
    """Return n leds of color divided over n steps scaled by maximum
    rotated by rot."""
    leds = [tuple(), ] * n
    for i in range(len(leds)):
        scaled = [ compress(c / 255 * i / n, maximum) for c in color ]
        leds[i] = tuple(scaled)
    # Rotate by rot.
    return leds[rot: ] + leds[: rot]

# Code in a 'while True:' loop repeats forever
while True:

    # Set random leds and shuffle every delay ms until button_a pressed.
    leds = random_lights(n, maximum)
    while not button_a.was_pressed():
        leds = shuffle(leds)
        for j in range(len(leds)):
            np[j] = leds[j]
        np.show()
        sleep(delay)

    # Set chasing leds every delay ms until button_a pressed.
    chase = 0
    while not button_a.was_pressed():
        leds = chase_lights(chase, (0, 255, 0, ))   # chase green leds
        for j in range(len(leds)):
            np[j] = leds[j]
        np.show()
        sleep(delay)
        chase = (chase + 1) % 12

1234567890123456789012345678901234567890123456789012345678901234567890

Links

Link Description
https://www.desmos.com/​calculator/​gdcw7rndv2 Desmos exponential compression graph
https://universal-solder.ca/​12-led-ring-ws2812b-rgb-addressable-50mm/​)) 50mm 12 NeoPixel (WS2812B) Ring available from Universal Solder
https://photos.app.goo.gl/​hWqZB8di2HHBCWo19 A 50mm 12 NeoPixel (WS2812B) Ring with soldered 0.1” headers and wired through a KS0360 Keyestudio Sensor Shield V2
https://photos.app.goo.gl/​zHPtcBgjWh99wYCZ6 micro:bit examples videos (NeoPixel Ring & Snake)
https://tech.microbit.org/​hardware/​edgeconnector/​ micro:bit pinouts
https://microbit-micropython.readthedocs.io/​en/​v2-docs/​neopixel.html micro:bit Python NeoPixel
https://docs.python.org/​3.4/​ Python 3.4 documentation

#microbit #embedded #Python