Compare commits

...

10 Commits

Author SHA1 Message Date
10db8f6d89
implement party mode 2024-04-03 00:57:50 -06:00
4a29e0c600
swap inside brightness and party buttons
- The outside button is next to the outside light, but not the inside button. This moves it beside the light and puts the party button in the middle (on the prototype board)
2024-04-03 00:56:44 -06:00
69042cbe9c
make brightness values configurable
- Also simplify the cycleBrightness() function with a loop
- Adjusted some of the brightness values
2024-04-02 23:57:32 -06:00
972b395f5d
better encapsulate InitPWM() 2024-04-02 23:43:42 -06:00
772ac6a8d8
add select block to button press channel
- outchan is unbuffered, so drop the message if there's already one sitting there.
2024-04-02 16:53:35 -06:00
67607347e4
remove time.Sleep() calls
- time.Sleep() blocks the entire program, other goroutines won't run. Instead, watch the button channels and filter out any extra presses within too short of a time.
2024-04-02 12:58:16 -06:00
3c4b177360
remove redundant goroutines watching brightness changes 2024-04-01 07:08:47 -06:00
bbd231b2eb
change ledset to a receiver on led 2024-04-01 06:25:20 -06:00
856a6e2d88
refactor lighthardware struct
- add led struct for individual colour LEDs
2024-04-01 06:19:19 -06:00
32d03ddd05
refactor to support multiple modes 2024-04-01 03:46:42 -06:00

361
main.go
View File

@ -2,6 +2,7 @@ package main
import ( import (
"machine" "machine"
"runtime"
"time" "time"
) )
@ -11,32 +12,44 @@ type pwm interface {
Channel(machine.Pin) (uint8, error) Channel(machine.Pin) (uint8, error)
} }
type led struct {
pin machine.Pin
pwm pwm
channel uint8
}
func (l *led) InitPWM() error {
var err error
l.channel, err = l.pwm.Channel(l.pin)
return err
}
func (l *led) set(brightness uint32) {
l.pwm.Set(l.channel, (l.pwm.Top()/brightnesspeak)*brightness)
}
type lighthardware struct { type lighthardware struct {
rPin machine.Pin red led
gPin machine.Pin green led
bPin machine.Pin blue led
rPwm pwm
gPwm pwm
bPwm pwm
rCh uint8
gCh uint8
bCh uint8
} }
func (lhw *lighthardware) InitPWM() error { func (lhw *lighthardware) InitPWM() error {
var err error var err error
lhw.rCh, err = lhw.rPwm.Channel(lhw.rPin) err = lhw.red.InitPWM()
if err != nil { if err != nil {
return err return err
} }
lhw.gCh, err = lhw.gPwm.Channel(lhw.gPin) err = lhw.green.InitPWM()
if err != nil { if err != nil {
return err return err
} }
lhw.bCh, err = lhw.bPwm.Channel(lhw.bPin) err = lhw.blue.InitPWM()
if err != nil { if err != nil {
return err return err
} }
@ -44,15 +57,36 @@ func (lhw *lighthardware) InitPWM() error {
return nil return nil
} }
type lightSet struct {
inside *lighthardware
inside2 *lighthardware
outside *lighthardware
}
type brightnesses struct {
inside uint32
outside uint32
}
var (
brightnessSteps = []uint32{0, 255, 100, 20}
)
const ( const (
period = uint64(1e9 / 500) period = uint64(1e9 / 500)
pressdelay = time.Millisecond * 500 pressdelay = time.Millisecond * 300
brightnesspeak = uint32(255) brightnesspeak = uint32(256)
defaultrng = uint32(time.Millisecond * 250)
maxrng = uint32(time.Millisecond * 500)
minrng = uint32(time.Millisecond * 50)
) )
func main() { func main() {
errs := make(chan error) errs := make(chan error)
outsidepushed_raw := make(chan bool)
insidepushed_raw := make(chan bool)
partypushed_raw := make(chan bool)
outsidepushed := make(chan bool) outsidepushed := make(chan bool)
insidepushed := make(chan bool) insidepushed := make(chan bool)
partypushed := make(chan bool) partypushed := make(chan bool)
@ -61,8 +95,8 @@ func main() {
insidebrightness := make(chan uint32, 2) insidebrightness := make(chan uint32, 2)
outsidebutton := machine.GP22 outsidebutton := machine.GP22
insidebutton := machine.GP21 insidebutton := machine.GP20
partybutton := machine.GP20 partybutton := machine.GP21
outsidebutton.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) outsidebutton.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
insidebutton.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) insidebutton.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
@ -70,23 +104,27 @@ func main() {
outsidebutton.SetInterrupt(machine.PinFalling, func(p machine.Pin) { outsidebutton.SetInterrupt(machine.PinFalling, func(p machine.Pin) {
select { select {
case outsidepushed <- true: case outsidepushed_raw <- true:
default: default:
} }
}) })
insidebutton.SetInterrupt(machine.PinFalling, func(p machine.Pin) { insidebutton.SetInterrupt(machine.PinFalling, func(p machine.Pin) {
select { select {
case insidepushed <- true: case insidepushed_raw <- true:
default: default:
} }
}) })
partybutton.SetInterrupt(machine.PinFalling, func(p machine.Pin) { partybutton.SetInterrupt(machine.PinFalling, func(p machine.Pin) {
select { select {
case partypushed <- true: case partypushed_raw <- true:
default: default:
} }
}) })
go delayChan(outsidepushed_raw, outsidepushed)
go delayChan(insidepushed_raw, insidepushed)
go delayChan(partypushed_raw, partypushed)
pwm2 := machine.PWM2 pwm2 := machine.PWM2
pwm2.Configure(machine.PWMConfig{ pwm2.Configure(machine.PWMConfig{
Period: period, Period: period,
@ -109,12 +147,18 @@ func main() {
}) })
insideLight := lighthardware{ insideLight := lighthardware{
rPin: machine.GP11, red: led{
gPin: machine.GP13, pin: machine.GP11,
bPin: machine.GP12, pwm: pwm5,
rPwm: pwm5, },
gPwm: pwm6, green: led{
bPwm: pwm6, pin: machine.GP13,
pwm: pwm6,
},
blue: led{
pin: machine.GP12,
pwm: pwm6,
},
} }
err := insideLight.InitPWM() err := insideLight.InitPWM()
@ -122,12 +166,18 @@ func main() {
errs <- err errs <- err
} else { } else {
insideLight2 := lighthardware{ insideLight2 := lighthardware{
rPin: machine.GP8, red: led{
gPin: machine.GP10, pin: machine.GP8,
bPin: machine.GP9, pwm: pwm4,
rPwm: pwm4, },
gPwm: pwm5, green: led{
bPwm: pwm4, pin: machine.GP10,
pwm: pwm5,
},
blue: led{
pin: machine.GP9,
pwm: pwm4,
},
} }
err = insideLight2.InitPWM() err = insideLight2.InitPWM()
@ -135,21 +185,33 @@ func main() {
errs <- err errs <- err
} else { } else {
outsideLight := lighthardware{ outsideLight := lighthardware{
rPin: machine.GP5, red: led{
gPin: machine.GP7, pin: machine.GP5,
bPin: machine.GP6, pwm: pwm2,
rPwm: pwm2, },
gPwm: pwm3, green: led{
bPwm: pwm3, pin: machine.GP7,
pwm: pwm3,
},
blue: led{
pin: machine.GP6,
pwm: pwm3,
},
} }
err = outsideLight.InitPWM() err = outsideLight.InitPWM()
if err != nil { if err != nil {
errs <- err errs <- err
} else { } else {
lights := lightSet{
inside: &insideLight,
inside2: &insideLight2,
outside: &outsideLight,
}
go cycleBrightness(insidepushed, insidebrightness) go cycleBrightness(insidepushed, insidebrightness)
go cycleBrightness(outsidepushed, outsidebrightness) go cycleBrightness(outsidepushed, outsidebrightness)
go loop(&insideLight, &insideLight2, &outsideLight, insidebrightness, outsidebrightness) go loop(lights, insidebrightness, outsidebrightness, partypushed)
} }
} }
} }
@ -157,66 +219,195 @@ func main() {
println((<-errs).Error()) println((<-errs).Error())
} }
func cycleBrightness(pushchan chan bool, brightnesschan chan uint32) { func delayChan(inchan <-chan bool, outchan chan<- bool) {
brightnesschan <- 0 var last time.Time
for { for {
<-pushchan val := <-inchan
brightnesschan <- 255 now := time.Now()
time.Sleep(pressdelay) if now.Sub(last) > pressdelay {
<-pushchan last = now
brightnesschan <- 120 select {
time.Sleep(pressdelay) case outchan <- val:
<-pushchan default:
brightnesschan <- 30 }
time.Sleep(pressdelay) }
<-pushchan
brightnesschan <- 0
time.Sleep(pressdelay)
} }
} }
func ledset(lpwm pwm, ledch uint8, brightness uint32) { func cycleBrightness(pushchan <-chan bool, brightnesschan chan<- uint32) {
lpwm.Set(ledch, (lpwm.Top()/brightnesspeak)*brightness) for {
for _, b := range brightnessSteps {
brightnesschan <- b
<-pushchan
}
}
} }
func loop(inside *lighthardware, inside2 *lighthardware, outside *lighthardware, insidebrightness chan uint32, outsidebrightness chan uint32) { func loop(lights lightSet, insidebrightness chan uint32, outsidebrightness chan uint32, partypushed <-chan bool) {
partyChange := make(chan bool)
lastBrightness := make(chan brightnesses)
for {
go normal(lights, insidebrightness, outsidebrightness, partyChange, lastBrightness)
<-partypushed
partyChange <- true
lb := <-lastBrightness
insidebrightness <- lb.inside
outsidebrightness <- lb.outside
go party(lights, insidebrightness, outsidebrightness, partyChange, lastBrightness)
<-partypushed
partyChange <- true
lb = <-lastBrightness
insidebrightness <- lb.inside
outsidebrightness <- lb.outside
}
}
func normal(lights lightSet, inBrightChange <-chan uint32, outBrightChange <-chan uint32, partyChange <-chan bool, lastBrightness chan<- brightnesses) {
var brightIn uint32 var brightIn uint32
var brightOut uint32 var brightOut uint32
inBrightChange := make(chan bool)
outBrightChange := make(chan bool)
go func() {
for {
brightIn = <-insidebrightness
select {
case inBrightChange <- true:
default:
}
}
}()
go func() {
for {
brightOut = <-outsidebrightness
select {
case outBrightChange <- true:
default:
}
}
}()
for { for {
select { select {
case <-inBrightChange: case brightIn = <-inBrightChange:
normal(inside, brightIn) setNormal(lights.inside, brightIn)
normal(inside2, brightIn) setNormal(lights.inside2, brightIn)
case <-outBrightChange: case brightOut = <-outBrightChange:
normal(outside, brightOut) setNormal(lights.outside, brightOut)
case <-partyChange:
lastBrightness <- brightnesses{inside: brightIn, outside: brightOut}
return
} }
} }
} }
func normal(light *lighthardware, brightness uint32) { func setNormal(light *lighthardware, brightness uint32) {
ledset(light.rPwm, light.rCh, brightness) light.red.set(brightness)
ledset(light.gPwm, light.gCh, brightness) light.green.set(brightness)
ledset(light.bPwm, light.bCh, brightness) light.blue.set(brightness)
}
func party(lights lightSet, inBrightChange <-chan uint32, outBrightChange <-chan uint32, partyChange <-chan bool, lastBrightness chan<- brightnesses) {
var brightIn uint32
var brightOut uint32
inBright := make(chan uint32, 1)
in2Bright := make(chan uint32, 1)
outBright := make(chan uint32, 1)
partysOver := make(chan bool, 3)
partysDone := make(chan bool, 3)
go partyLight(lights.inside, inBright, partysOver, partysDone)
go partyLight(lights.inside2, in2Bright, partysOver, partysDone)
go partyLight(lights.outside, outBright, partysOver, partysDone)
runtime.Gosched()
for {
select {
case brightIn = <-inBrightChange:
inBright <- brightIn
in2Bright <- brightIn
case brightOut = <-outBrightChange:
outBright <- brightOut
case <-partyChange:
partysOver <- true
partysOver <- true
partysOver <- true
runtime.Gosched()
<-partysDone
<-partysDone
<-partysDone
lastBrightness <- brightnesses{inside: brightIn, outside: brightOut}
return
}
}
}
func partyLight(light *lighthardware, brightChange <-chan uint32, endParty <-chan bool, partyEnded chan<- bool) {
rBright := make(chan uint32, 1)
gBright := make(chan uint32, 1)
bBright := make(chan uint32, 1)
partysOver := make(chan bool, 3)
partysDone := make(chan bool, 3)
go partyColour(light.red, rBright, partysOver, partysDone)
go partyColour(light.green, gBright, partysOver, partysDone)
go partyColour(light.blue, bBright, partysOver, partysDone)
runtime.Gosched()
for {
select {
case brightness := <-brightChange:
rBright <- brightness
gBright <- brightness
bBright <- brightness
case <-endParty:
partysOver <- true
partysOver <- true
partysOver <- true
runtime.Gosched()
<-partysDone
<-partysDone
<-partysDone
partyEnded <- true
return
}
}
}
func partyColour(light led, brightChange <-chan uint32, endParty <-chan bool, partyEnded chan<- bool) {
brightness := <-brightChange
LOOP:
for {
if brightness > 0 {
r, err := machine.GetRNG()
if err != nil {
r = defaultrng
}
r = (r % maxrng) + minrng
delaytime := time.Duration(r / brightness)
for i := brightness; i > 0; i-- {
light.set(i)
runtime.Gosched()
time.Sleep(delaytime)
select {
case brightness = <-brightChange:
continue LOOP
case <-endParty:
partyEnded <- true
return
default:
}
}
for i := uint32(0); i <= brightness; i++ {
light.set(i)
runtime.Gosched()
time.Sleep(delaytime)
select {
case brightness = <-brightChange:
continue LOOP
case <-endParty:
partyEnded <- true
return
default:
}
}
} else {
light.set(0)
select {
case brightness = <-brightChange:
continue
case <-endParty:
partyEnded <- true
return
}
}
}
} }