cabin-lights/main.go

414 lines
8.3 KiB
Go

package main
import (
"machine"
"runtime"
"time"
)
type pwm interface {
Top() uint32
Set(uint8, uint32)
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 {
red led
green led
blue led
}
func (lhw *lighthardware) InitPWM() error {
var err error
err = lhw.red.InitPWM()
if err != nil {
return err
}
err = lhw.green.InitPWM()
if err != nil {
return err
}
err = lhw.blue.InitPWM()
if err != nil {
return err
}
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 (
period = uint64(1e9 / 500)
pressdelay = time.Millisecond * 300
brightnesspeak = uint32(256)
defaultrng = uint32(time.Millisecond * 250)
maxrng = uint32(time.Millisecond * 500)
minrng = uint32(time.Millisecond * 50)
)
func main() {
errs := make(chan error)
outsidepushed_raw := make(chan bool)
insidepushed_raw := make(chan bool)
partypushed_raw := make(chan bool)
outsidepushed := make(chan bool)
insidepushed := make(chan bool)
partypushed := make(chan bool)
outsidebrightness := make(chan uint32, 2)
insidebrightness := make(chan uint32, 2)
outsidebutton := machine.GP22
insidebutton := machine.GP20
partybutton := machine.GP21
outsidebutton.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
insidebutton.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
partybutton.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
outsidebutton.SetInterrupt(machine.PinFalling, func(p machine.Pin) {
select {
case outsidepushed_raw <- true:
default:
}
})
insidebutton.SetInterrupt(machine.PinFalling, func(p machine.Pin) {
select {
case insidepushed_raw <- true:
default:
}
})
partybutton.SetInterrupt(machine.PinFalling, func(p machine.Pin) {
select {
case partypushed_raw <- true:
default:
}
})
go delayChan(outsidepushed_raw, outsidepushed)
go delayChan(insidepushed_raw, insidepushed)
go delayChan(partypushed_raw, partypushed)
pwm2 := machine.PWM2
pwm2.Configure(machine.PWMConfig{
Period: period,
})
pwm3 := machine.PWM3
pwm3.Configure(machine.PWMConfig{
Period: period,
})
pwm4 := machine.PWM4
pwm4.Configure(machine.PWMConfig{
Period: period,
})
pwm5 := machine.PWM5
pwm5.Configure(machine.PWMConfig{
Period: period,
})
pwm6 := machine.PWM6
pwm6.Configure(machine.PWMConfig{
Period: period,
})
insideLight := lighthardware{
red: led{
pin: machine.GP11,
pwm: pwm5,
},
green: led{
pin: machine.GP13,
pwm: pwm6,
},
blue: led{
pin: machine.GP12,
pwm: pwm6,
},
}
err := insideLight.InitPWM()
if err != nil {
errs <- err
} else {
insideLight2 := lighthardware{
red: led{
pin: machine.GP8,
pwm: pwm4,
},
green: led{
pin: machine.GP10,
pwm: pwm5,
},
blue: led{
pin: machine.GP9,
pwm: pwm4,
},
}
err = insideLight2.InitPWM()
if err != nil {
errs <- err
} else {
outsideLight := lighthardware{
red: led{
pin: machine.GP5,
pwm: pwm2,
},
green: led{
pin: machine.GP7,
pwm: pwm3,
},
blue: led{
pin: machine.GP6,
pwm: pwm3,
},
}
err = outsideLight.InitPWM()
if err != nil {
errs <- err
} else {
lights := lightSet{
inside: &insideLight,
inside2: &insideLight2,
outside: &outsideLight,
}
go cycleBrightness(insidepushed, insidebrightness)
go cycleBrightness(outsidepushed, outsidebrightness)
go loop(lights, insidebrightness, outsidebrightness, partypushed)
}
}
}
println((<-errs).Error())
}
func delayChan(inchan <-chan bool, outchan chan<- bool) {
var last time.Time
for {
val := <-inchan
now := time.Now()
if now.Sub(last) > pressdelay {
last = now
select {
case outchan <- val:
default:
}
}
}
}
func cycleBrightness(pushchan <-chan bool, brightnesschan chan<- uint32) {
for {
for _, b := range brightnessSteps {
brightnesschan <- b
<-pushchan
}
}
}
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 brightOut uint32
for {
select {
case brightIn = <-inBrightChange:
setNormal(lights.inside, brightIn)
setNormal(lights.inside2, brightIn)
case brightOut = <-outBrightChange:
setNormal(lights.outside, brightOut)
case <-partyChange:
lastBrightness <- brightnesses{inside: brightIn, outside: brightOut}
return
}
}
}
func setNormal(light *lighthardware, brightness uint32) {
light.red.set(brightness)
light.green.set(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
}
}
}
}