hikari/main.go
2026-05-06 17:36:12 +02:00

244 lines
4.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/ws2812"
)
const (
numLEDs = 20
numRows = 4
numCols = 5
numEffects = 7
frameDelay = 20 * time.Millisecond
debounce = 200 * time.Millisecond
numStars = 8
starStep = 7
)
const (
pinLED = machine.D6
pinBtn = machine.D3
)
type star struct {
pos uint8 // LED index
hue uint8 // colour on the wheel
br uint8 // current brightness (0-255)
dir int8 // +1 rising, -1 falling
}
var (
starList [numStars]star
starsOK bool
leds [numLEDs]color.RGBA
strip ws2812.Device
// xorshift32 PRNG seeded with a compile-time constant
rngState uint32 = 0xDEADBEEF
)
// wheel maps 0-255 → smooth full-hue spectrum (R→G→B→R)
func wheel(pos uint8) color.RGBA {
switch {
case pos < 85:
return color.RGBA{R: pos * 3, G: 255 - pos*3, B: 0}
case pos < 170:
pos -= 85
return color.RGBA{R: 255 - pos*3, G: 0, B: pos * 3}
default:
pos -= 170
return color.RGBA{R: 0, G: pos * 3, B: 255 - pos*3}
}
}
// dim scales a colour by brightness (0-255)
func dim(c color.RGBA, brightness uint8) color.RGBA {
return color.RGBA{
R: uint8(uint16(c.R) * uint16(brightness) >> 8),
G: uint8(uint16(c.G) * uint16(brightness) >> 8),
B: uint8(uint16(c.B) * uint16(brightness) >> 8),
}
}
// idx converts grid coordinates to LED index (simple row-major)
func idx(row, col int) int { return row*numCols + col }
// clear sets all LEDs to off
func clear() {
for i := range leds {
leds[i] = color.RGBA{}
}
}
// rand returns the next pseudo-random uint32
func rand() uint32 {
rngState ^= rngState << 13
rngState ^= rngState >> 17
rngState ^= rngState << 5
return rngState
}
// effectRainbow cycles through all the colors slowly
func effectRainbow(step uint32) {
c := wheel(uint8(step >> 2)) // full cycle ≈ 20 s
for i := range leds {
leds[i] = c
}
}
// effectRowSweep lit a row at a time, the color/hue changes
func effectRowSweep(step uint32) {
clear()
row := int(step/40) % numRows
c := wheel(uint8(step >> 1))
for col := 0; col < numCols; col++ {
leds[idx(row, col)] = c
}
}
// effectColSweep lit a column at a time, the color/hue changes
func effectColSweep(step uint32) {
clear()
col := int(step/40) % numCols
c := wheel(uint8(step >> 1))
for row := 0; row < numRows; row++ {
leds[idx(row, col)] = c
}
}
// initStars resets the data for the stars effect
func initStars() {
for i := range starList {
starList[i] = star{
pos: uint8(rand() % numLEDs),
hue: uint8(rand()),
br: uint8(i * (255 / numStars)),
dir: 1,
}
}
starsOK = true
}
// effectStarts lit random LEDs and dimm them slowly
func effectStars(_ uint32) {
if !starsOK {
initStars()
}
clear()
for i := range starList {
s := &starList[i]
// Advance brightness
nb := int16(s.br) + int16(s.dir)*starStep
switch {
case nb >= 255:
s.br = 255
s.dir = -1
case nb <= 0:
// Star died pick a new random one
s.br = 0
s.dir = 1
s.pos = uint8(rand() % numLEDs)
s.hue = uint8(rand())
default:
s.br = uint8(nb)
}
leds[s.pos] = dim(wheel(s.hue), s.br)
}
}
// effectBreathing shows classic breathing effect
func effectBreathing(step uint32) {
t := uint8(step)
var br uint8
if t < 128 {
br = t * 2
} else {
br = (255 - t) * 2
}
hue := uint8(step >> 7)
for i := range leds {
leds[i] = dim(wheel(hue), br)
}
}
// effectRainbowRows shows a different color in each row and change its hue slowly
func effectRainbowRows(step uint32) {
base := uint8(step >> 2)
for row := 0; row < numRows; row++ {
c := wheel(base + uint8(row*64))
for col := 0; col < numCols; col++ {
leds[idx(row, col)] = c
}
}
}
// effectWave runs a cool wave effect
func effectWave(step uint32) {
clear()
head := int(step/8) % numCols
hue := uint8(step >> 1)
bri := [numCols]uint8{255, 110, 35, 0, 0}
for col := 0; col < numCols; col++ {
dist := (head - col + numCols) % numCols
if dist >= numCols || bri[dist] == 0 {
continue
}
c := dim(wheel(hue+uint8(col*12)), bri[dist])
for row := 0; row < numRows; row++ {
leds[idx(row, col)] = c
}
}
}
func main() {
pinLED.Configure(machine.PinConfig{Mode: machine.PinOutput})
strip = ws2812.New(pinLED)
pinBtn.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
var (
effect int
step uint32
prevBtn bool
lastPress time.Time
)
for {
btnDown := !pinBtn.Get()
if btnDown && !prevBtn && time.Since(lastPress) > debounce {
effect = (effect + 1) % numEffects
lastPress = time.Now()
starsOK = false
}
prevBtn = btnDown
switch effect {
case 0:
effectRainbow(step)
case 1:
effectRowSweep(step)
case 2:
effectColSweep(step)
case 3:
effectStars(step)
case 4:
effectBreathing(step)
case 5:
effectRainbowRows(step)
case 6:
effectWave(step)
}
strip.WriteColors(leds[:])
step++
time.Sleep(frameDelay)
}
}