package main import ( "fmt" "log" "math" "os" "strconv" "time" patterns "git.martyn.berlin/martyn/LEDController/internal/patterns" queue "git.martyn.berlin/martyn/LEDController/internal/queue" remapping "git.martyn.berlin/martyn/LEDController/internal/remapping" webserver "git.martyn.berlin/martyn/LEDController/internal/webserver" "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) var universeCount int = 0 func foreverLoop() { for /*ever*/ { time.Sleep(40 * time.Millisecond) //25fps for u := 0; u < universeCount; 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 } universeCount = int(math.Ceil(float64(PanelHeight*PanelWidth*3) / 510)) fmt.Printf("Universe count is %d\n", universeCount) 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 < universeCount; 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 := reduceBrightness(remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaPanel(PanelWidth, PanelHeight, 40))), PanelBrightness) for i := 0; i < universeCount; i++ { currentFrame[i] = remapping.Slice512(rearranged[i]) }*/ 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 = "plasma" e.Speed = 40 globalEffectChannel <- e go func() { for /*ever*/ { if currentEffect.Duration > 0 { currentEffect.Duration -= 40 } else { if len(globalEffectChannel) > 0 { lastEffect := currentEffect currentEffect = <-globalEffectChannel if lastEffect != currentEffect { fmt.Printf("New effect selected : %s\n", currentEffect.Effect) } } } 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 for i := 0; i < universeCount; i++ { currentFrame[i] = reduceBrightness(remapping.Slice512(rearranged[i]), PanelBrightness) } <-sema time.Sleep(40 * time.Millisecond) } }() foreverLoop() }