package main import ( "fmt" "log" "time" "strconv" "os" webserver "git.martyn.berlin/martyn/LEDController/internal/webserver" queue "git.martyn.berlin/martyn/LEDController/internal/queue" remapping "git.martyn.berlin/martyn/LEDController/internal/remapping" patterns "git.martyn.berlin/martyn/LEDController/internal/patterns" "github.com/Hundemeier/go-sacn/sacn" ) type RGBcolor = [3]byte var currentFrame [2][512]byte var channels [2]chan<-[512]byte var sema = make(chan struct{}, 1) // a binary semaphore guarding currentFrame var currentEffect queue.QueueItem; var globalEffectChannel = make(chan queue.QueueItem, 1024) func foreverLoop() { for /*ever*/ { time.Sleep(40 * time.Millisecond) //25fps for u := 0; u < 2; u++ { sema <- struct{}{} // acquire token channels[u] <- currentFrame[u] <- sema } } } func reduceBrightness(universe [512]byte, percentage int) [512]byte{ for i := range(universe) { universe[i] = byte(float64(universe[i]) * (float64(percentage) / float64(100))) } return universe } func main() { go func() { var err error listenPort := 5353 if os.Getenv("LISTEN_PORT") != "" { listenPort, err = strconv.Atoi(os.Getenv("LISTEN_PORT")) if err != nil { listenPort = 5353 } } fmt.Printf("Starting webserver on port %d\n", listenPort) webserver.HandleHTTP(globalEffectChannel, listenPort) }() PanelIP := os.Getenv("PANEL_IP") if PanelIP == "" { PanelIP = "127.0.0.1" } PanelWidth, err := strconv.Atoi(os.Getenv("PANEL_WIDTH")) if err != nil { PanelWidth = 68 } PanelHeight, err := strconv.Atoi(os.Getenv("PANEL_HEIGHT")) if err != nil { PanelHeight = 4 } PanelBrightness, err := strconv.Atoi(os.Getenv("PANEL_BRIGHTNESS")) if err != nil { PanelBrightness = 100 } //instead of "" you could provide an ip-address that the socket should bind to trans, err := sacn.NewTransmitter("", [16]byte{1, 2}, "test") if err != nil { log.Fatal(err) } //activates the universes for i := 0; i < 2; i++ { channels[i], err = trans.Activate(uint16(i+1)) //deactivate the channel on exit defer close(channels[i]) trans.SetMulticast(uint16(i+1), false) //this specific setup will not multicast on windows, trans.SetDestinations(uint16(i+1), []string{PanelIP}) } // Plasma for start frame rearranged := remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaPanel(PanelWidth, PanelHeight, 40))) currentFrame[0] = remapping.Slice512(rearranged[0]) currentFrame[1] = remapping.Slice512(rearranged[1]) var e queue.QueueItem e.Effect = "red" e.Duration = 40 * 50 globalEffectChannel <- e e.Effect = "colour" e.Duration = 40 * 10 e.SeedColour[0] = 0 e.SeedColour[1] = 255 e.SeedColour[2] = 0 globalEffectChannel <- e e.Effect = "default" globalEffectChannel <- e go func() { for /*ever*/ { if currentEffect.Duration > 0 { currentEffect.Duration -= 40 } else { if len(globalEffectChannel) > 0 { currentEffect = <- globalEffectChannel } } var rearranged [][]byte switch currentEffect.Effect { case "line": rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.ZigZag(PanelWidth, PanelHeight))) case "plasma": rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaPanel(PanelWidth, PanelHeight, currentEffect.Speed))) case "red": rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.RedPanel(PanelWidth,PanelHeight))) case "random": rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.RandomColourPanel(PanelWidth,PanelHeight,currentEffect.Speed))) case "linearplasma": rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, patterns.LinearPlasma(PanelWidth*PanelHeight)) case "gradientred": rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, patterns.Gradient(255, 0, 0, 0, 0, 255, 0, PanelWidth*PanelHeight)) case "sine": var black patterns.RGBcolor var red patterns.RGBcolor red[0] = 255 rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.Sinewave(PanelWidth, PanelHeight, black, red, currentEffect.Speed))) case "sinechase": var black patterns.RGBcolor var red patterns.RGBcolor red[0] = 255 rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.SineChase(PanelWidth, PanelHeight, black, red, currentEffect.Speed))) case "plasmapulse": rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaColourPanel(PanelWidth,PanelHeight, currentEffect.Speed))) case "colour": rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.FillPanel(PanelWidth,PanelHeight, currentEffect.SeedColour[0], currentEffect.SeedColour[1], currentEffect.SeedColour[2]))) case "fade": rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.GradientPanel(PanelWidth,PanelHeight, currentEffect.SeedColour, currentEffect.SecondColour))) default: rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.FillPanel(PanelWidth,PanelHeight, 128,0,128))) } //rearranged := remapping.SliceRearrange(PanelWidth,PanelHeight,true,linearPlasma(PanelWidth*PanelHeight)) sema <- struct{}{} // acquire token currentFrame[0] = reduceBrightness(remapping.Slice512(rearranged[0]),PanelBrightness) currentFrame[1] = reduceBrightness(remapping.Slice512(rearranged[1]),PanelBrightness) <- sema time.Sleep(40 * time.Millisecond) } }() foreverLoop(); }