Skip to content

Commit 8da19a9

Browse files
gandarezclaude
andcommitted
ws2812: add Strip type with pixel buffer and PIO support for RP2040/RP2350
Merge the neopixel package functionality into ws2812 as a new Strip type, providing a higher-level API with pixel buffering, brightness control, and PIO-based hardware timing on RP2040/RP2350. On other platforms, Strip falls back to the existing bit-bang driver. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a514169 commit 8da19a9

File tree

9 files changed

+203
-2
lines changed

9 files changed

+203
-2
lines changed

.claude/settings.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(find /Users/gandarez/development/git/gandarez/tinygo-drivers -type f -name *ws2812*)",
5+
"Bash(grep -r \"RP2350\\\\|rp2350\" /Users/gandarez/development/git/gandarez/tinygo-drivers --include=*.md --include=*.go --include=*.txt)",
6+
"Bash(xargs ls:*)",
7+
"Bash(grep -r \"PIO\\\\|Programmable I/O\\\\|pio\\\\|pioasm\" /Users/gandarez/development/git/gandarez/tinygo-drivers --include=*.go --include=*.md)",
8+
"Read(//writeByte150/,/^func \\\\\\(d Device\\\\\\) writeByte[0-9]/**)",
9+
"Bash(wc -l /Users/gandarez/development/git/gandarez/tinygo-drivers/ws2812/*.go)",
10+
"Bash(tinygo version:*)",
11+
"Read(//usr/local/**)",
12+
"Read(//Users/gandarez/go/pkg/mod/github.com/tinygo-org/**)",
13+
"Bash(find ~/go/pkg/mod/github.com/tinygo-org/tinygo@v0.37.0/src/device -name *.go)",
14+
"Read(//Users/gandarez/go/pkg/mod/**)",
15+
"Bash(grep -r 'type PIO' ~/go/pkg/mod/github.com/tinygo-org/tinygo@v0.37.0/src --include=*.go)",
16+
"Bash(grep -r \"rp\\\\.PIO0\\\\|rp\\\\.PIO1\" ~/go/pkg/mod/github.com/tinygo-org/tinygo@v0.37.0 --include=*.go)",
17+
"Bash(grep -r \"PIO0_Type\\\\|type.*PIO\" ~/go/pkg/mod/github.com/tinygo-org/tinygo@v0.37.0 --include=*.go)",
18+
"Bash(find ~/go/pkg/mod/github.com/tinygo-org/tinygo@v0.37.0 -name *.svd)",
19+
"Bash(find ~/go/pkg/mod/github.com/tinygo-org/tinygo@v0.37.0 -path */RP* -o -path */rp2*)",
20+
"Bash(grep -i \"\\\\.go$\")",
21+
"Bash(grep -r //go:generate ~/go/pkg/mod/github.com/tinygo-org/tinygo@v0.37.0/src/device --include=*.go)",
22+
"Bash(find ~/go/pkg/mod/github.com/tinygo-org/tinygo@v0.37.0 -name rp*.go -o -name *rp2*.go)",
23+
"Bash(grep -r PIO0_Type ~/go/pkg/mod/github.com/tinygo-org --include=*.go)",
24+
"Read(//Users/gandarez/**)",
25+
"Bash(PATH=\"/Users/gandarez/sdk/go1.25.0/bin:$PATH\" which tinygo)",
26+
"Bash(PATH=\"/Users/gandarez/sdk/go1.25.0/bin:$PATH\" tinygo version)",
27+
"Read(//opt/**)",
28+
"WebFetch(domain:raw.githubusercontent.com)",
29+
"Bash(gh api:*)",
30+
"Bash(gh auth:*)",
31+
"WebFetch(domain:github.com)",
32+
"Bash(go get:*)",
33+
"Bash(ls /Users/gandarez/sdk/go*/bin/go)",
34+
"Bash(/Users/gandarez/sdk/go1.25.0/bin/go get:*)",
35+
"Bash(/Users/gandarez/sdk/go1.25.0/bin/go mod:*)",
36+
"Bash(/Users/gandarez/sdk/go1.25.0/bin/go vet:*)",
37+
"Bash(git commit:*)",
38+
"Bash(git push:*)",
39+
"Bash(/opt/homebrew/bin/gh pr:*)",
40+
"Bash(gh pr:*)"
41+
]
42+
}
43+
}

examples/ws2812-strip/main.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Demonstrates WS2812B RGB LED control using the Strip API.
2+
//
3+
// Tested on Waveshare RP2350-LCD-1.47 with onboard RGB LED on GP22.
4+
//
5+
// To flash:
6+
//
7+
// tinygo flash -target=pico2 ./examples/ws2812-strip/
8+
package main
9+
10+
import (
11+
"image/color"
12+
"machine"
13+
"time"
14+
15+
"tinygo.org/x/drivers/ws2812"
16+
)
17+
18+
func main() {
19+
strip, err := ws2812.NewStrip(machine.GP22, 1)
20+
if err != nil {
21+
panic(err)
22+
}
23+
strip.SetBrightness(50)
24+
25+
colors := []color.RGBA{
26+
{R: 255, G: 0, B: 0},
27+
{R: 0, G: 255, B: 0},
28+
{R: 0, G: 0, B: 255},
29+
}
30+
31+
for i := 0; ; i = (i + 1) % 3 {
32+
strip.SetPixel(0, colors[i])
33+
strip.Show()
34+
time.Sleep(time.Second)
35+
}
36+
}

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
module tinygo.org/x/drivers
22

3-
43
go 1.22.1
54

65
toolchain go1.23.1
76

8-
97
require (
108
github.com/eclipse/paho.mqtt.golang v1.2.0
119
github.com/frankban/quicktest v1.10.2
1210
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1311
github.com/orsinium-labs/tinymath v1.1.0
1412
github.com/soypat/natiu-mqtt v0.5.1
13+
github.com/tinygo-org/pio v0.3.0
1514
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
1615
golang.org/x/net v0.33.0
1716
tinygo.org/x/tinyfont v0.3.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ github.com/orsinium-labs/tinymath v1.1.0 h1:KomdsyLHB7vE3f1nRAJF2dyf1m/gnM2HxfTe
1717
github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A=
1818
github.com/soypat/natiu-mqtt v0.5.1 h1:rwaDmlvjzD2+3MCOjMZc4QEkDkNwDzbct2TJbpz+TPc=
1919
github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4=
20+
github.com/tinygo-org/pio v0.3.0 h1:opEnOtw58KGB4RJD3/n/Rd0/djYGX3DeJiXLI6y/yDI=
21+
github.com/tinygo-org/pio v0.3.0/go.mod h1:wf6c6lKZp+pQOzKKcpzchmRuhiMc27ABRuo7KVnaMFU=
2022
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
2123
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
2224
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=

smoketest.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ tinygo build -size short -o ./build/test.bin -target=m5stamp-c3 ./examp
9393
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
9494
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/ws2812
9595
tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
96+
tinygo build -size short -o ./build/test.bin -target=pico2 ./examples/ws2812-strip
9697
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go
9798
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/microphone/main.go
9899
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/buzzer/main.go

ws2812/strip.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package ws2812
2+
3+
import "image/color"
4+
5+
// Strip controls a strip of WS2812B LEDs with a pixel buffer and brightness control.
6+
// Use NewStrip to create a new instance.
7+
type Strip struct {
8+
pixels []color.RGBA
9+
brightness uint8
10+
writeFunc func(pixels []color.RGBA, brightness uint8) error
11+
}
12+
13+
// SetPixel sets the color of pixel at index i.
14+
func (s *Strip) SetPixel(i int, c color.RGBA) {
15+
if i >= 0 && i < len(s.pixels) {
16+
s.pixels[i] = c
17+
}
18+
}
19+
20+
// Show sends the current pixel buffer to the LED strip.
21+
func (s *Strip) Show() error {
22+
return s.writeFunc(s.pixels, s.brightness)
23+
}
24+
25+
// SetBrightness sets the global brightness (0-255).
26+
func (s *Strip) SetBrightness(b uint8) {
27+
s.brightness = b
28+
}
29+
30+
// Fill sets all pixels to the given color.
31+
func (s *Strip) Fill(c color.RGBA) {
32+
for i := range s.pixels {
33+
s.pixels[i] = c
34+
}
35+
}
36+
37+
// Clear turns off all pixels.
38+
func (s *Strip) Clear() {
39+
s.Fill(color.RGBA{})
40+
}
41+
42+
// NumPixels returns the number of pixels in the strip.
43+
func (s *Strip) NumPixels() int {
44+
return len(s.pixels)
45+
}
46+
47+
// applyBrightness scales a color by the brightness value.
48+
func applyBrightness(c color.RGBA, brightness uint8) (r, g, b uint8) {
49+
r = uint8((uint16(c.R) * uint16(brightness)) >> 8)
50+
g = uint8((uint16(c.G) * uint16(brightness)) >> 8)
51+
b = uint8((uint16(c.B) * uint16(brightness)) >> 8)
52+
return
53+
}

ws2812/strip_other.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//go:build !rp2040 && !rp2350
2+
3+
package ws2812
4+
5+
import (
6+
"image/color"
7+
"machine"
8+
)
9+
10+
// NewStrip creates a new WS2812B LED strip on the given pin with the specified
11+
// number of pixels. On platforms without PIO, it uses the bit-bang driver.
12+
func NewStrip(pin machine.Pin, numPixels int) (*Strip, error) {
13+
pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
14+
dev := NewWS2812(pin)
15+
return &Strip{
16+
pixels: make([]color.RGBA, numPixels),
17+
brightness: 255,
18+
writeFunc: func(pixels []color.RGBA, brightness uint8) error {
19+
adjusted := make([]color.RGBA, len(pixels))
20+
for i, c := range pixels {
21+
r, g, bl := applyBrightness(c, brightness)
22+
adjusted[i] = color.RGBA{R: r, G: g, B: bl}
23+
}
24+
return dev.WriteColors(adjusted)
25+
},
26+
}, nil
27+
}

ws2812/strip_rp2.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//go:build rp2040 || rp2350
2+
3+
package ws2812
4+
5+
import (
6+
"image/color"
7+
"machine"
8+
9+
pio "github.com/tinygo-org/pio/rp2-pio"
10+
"github.com/tinygo-org/pio/rp2-pio/piolib"
11+
)
12+
13+
// NewStrip creates a new WS2812B LED strip on the given pin with the specified
14+
// number of pixels. On RP2040/RP2350, it uses PIO for hardware-timed control.
15+
func NewStrip(pin machine.Pin, numPixels int) (*Strip, error) {
16+
sm, err := pio.PIO0.ClaimStateMachine()
17+
if err != nil {
18+
return nil, err
19+
}
20+
ws, err := piolib.NewWS2812B(sm, pin)
21+
if err != nil {
22+
return nil, err
23+
}
24+
return &Strip{
25+
pixels: make([]color.RGBA, numPixels),
26+
brightness: 255,
27+
writeFunc: func(pixels []color.RGBA, brightness uint8) error {
28+
for _, c := range pixels {
29+
r, g, bl := applyBrightness(c, brightness)
30+
ws.PutRGB(r, g, bl)
31+
}
32+
return nil
33+
},
34+
}, nil
35+
}

ws2812/ws2812.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
// Package ws2812 implements a driver for WS2812 and SK6812 RGB LED strips.
2+
//
3+
// For low-level control, use Device with NewWS2812 or NewSK6812.
4+
// For a higher-level API with pixel buffering and brightness control,
5+
// use Strip with NewStrip. On RP2040/RP2350, Strip uses PIO for
6+
// hardware-timed control; on other platforms it falls back to bit-banging.
27
package ws2812 // import "tinygo.org/x/drivers/ws2812"
38

49
//go:generate go run gen-ws2812.go -arch=cortexm 16 48 64 120 125 150 168 200

0 commit comments

Comments
 (0)