package main import ( "flag" "fmt" "image" "image/color" _ "image/jpeg" "log" "os" "os/signal" "syscall" "time" rgbmatrix "gitea.wagshome.duckdns.org/publicWagsHome/go-rpi-rgb-led-matrix" "github.com/disintegration/imaging" MQTT "github.com/eclipse/paho.mqtt.golang" mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/fogleman/gg" ) type Animation struct { ctx *gg.Context position image.Point dir image.Point height int width int stroke int image []image.Image updown int msg chan mqtt.Message } var ( rows = flag.Int("led-rows", 32, "number of rows supported") cols = flag.Int("led-cols", 32, "number of columns supported") parallel = flag.Int("led-parallel", 1, "number of daisy-chained panels") chain = flag.Int("led-chain", 2, "number of displays daisy-chained") brightness = flag.Int("brightness", 100, "brightness (0-100)") hardware_mapping = flag.String("led-gpio-mapping", "regular", "Name of GPIO mapping used.") show_refresh = flag.Bool("led-show-refresh", false, "Show refresh rate.") inverse_colors = flag.Bool("led-inverse", false, "Switch if your matrix has inverse colors on.") disable_hardware_pulsing = flag.Bool("led-no-hardware-pulse", false, "Don't use hardware pin-pulse generation.") ) func main() { config := &rgbmatrix.DefaultConfig config.Rows = *rows config.Cols = *cols config.Parallel = *parallel config.ChainLength = *chain config.Brightness = *brightness config.HardwareMapping = *hardware_mapping config.ShowRefreshRate = *show_refresh config.InverseColors = *inverse_colors config.DisableHardwarePulsing = *disable_hardware_pulsing setupMQTT() /*bounds := mario.Bounds() var histogram [16][4]int for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { r, g, b, a := mario.At(x, y).RGBA() // A color's RGBA method returns values in the range [0, 65535]. // Shifting by 12 reduces this to the range [0, 15]. histogram[r>>12][0]++ histogram[g>>12][1]++ histogram[b>>12][2]++ histogram[a>>12][3]++ } }*/ m, err := rgbmatrix.NewRGBLedMatrix(config) fatal(err) mqMessages := make(chan mqtt.Message) log.Println("making listener") go listener(mqMessages) tk := rgbmatrix.NewToolKit(m) defer tk.Close() log.Println("making animator") go animator(tk, mqMessages) log.Println("I guess I'm at the end") sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) done := make(chan bool, 1) go func() { sig := <-sigs fmt.Println() fmt.Println(sig) done <- true }() fmt.Println("awaiting signal") <-done fmt.Println("exiting") } func listener(mqMessages chan mqtt.Message) { opts := setupMQTT() client := MQTT.NewClient(opts) topic := "home/rgbboard" if token := client.Connect(); token.Wait() && token.Error() != nil { log.Println(fmt.Sprintf("failed to connect to mq: %s", token.Error().Error())) panic(token.Error()) } log.Println("connected") client.Subscribe(topic, 0, func(client mqtt.Client, msg mqtt.Message) { log.Println("Receiving ", string(msg.Payload()), " on topic: ", msg.Topic()) mqMessages <- msg }) } func animator(tk *rgbmatrix.ToolKit, mqMessages chan mqtt.Message) { tk.PlayAnimation(NewAnimation(image.Point{64, 32}, mqMessages)) } func setupMQTT() *mqtt.ClientOptions { opts := MQTT.NewClientOptions() opts.AddBroker(fmt.Sprintf("tcp://%s:%s", os.Getenv("MQTTBROKER"), os.Getenv("MQTTPORT"))) opts.SetUsername(os.Getenv("MQTTUSER")) opts.SetPassword(os.Getenv("MQTTPASS")) opts.SetClientID("rgbboard") return opts } func init() { flag.Parse() } func fatal(err error) { if err != nil { panic(err) } } func NewAnimation(sz image.Point, mqMessages chan mqtt.Message) *Animation { reader, err := os.Open("marioUp.png") if err != nil { log.Fatal(err) } rawMario, _, err := image.Decode(reader) //marioUp := imaging.FlipH(imaging.Resize(rawMario, 16, 16, imaging.Lanczos)) marioUp := imaging.Resize(rawMario, 16, 16, imaging.Lanczos) reader, err = os.Open("marioDown.png") if err != nil { log.Fatal(err) } rawMario, _, err = image.Decode(reader) //marioDown := imaging.FlipH(imaging.Resize(rawMario, 16, 16, imaging.Lanczos)) marioDown := imaging.Resize(rawMario, 16, 16, imaging.Lanczos) images := []image.Image{marioUp, marioDown} return &Animation{ ctx: gg.NewContext(sz.X, sz.Y), dir: image.Point{1, 1}, height: 8, width: 8, stroke: 8, image: images, updown: 0, msg: mqMessages, } } func (a *Animation) Next() (image.Image, <-chan time.Time, error) { defer a.updatePosition() a.ctx.SetColor(color.Black) a.ctx.Clear() if a.dir.X == 1 { a.ctx.DrawImageAnchored(a.image[a.updown], a.position.X, a.position.Y, 0.5, 0.5) } else { a.ctx.DrawImageAnchored(imaging.FlipH(a.image[a.updown]), a.position.X, a.position.Y, 0.5, 0.5) } a.ctx.SetColor(color.White) select { case msg := <-a.msg: a.ctx.DrawString(string(msg.Payload()), 5, 9) default: } return a.ctx.Image(), time.After(time.Millisecond * 50), nil } func (a *Animation) updatePosition() { a.position.X += 1 * a.dir.X a.position.Y += 1 * a.dir.Y if a.position.Y+a.height > a.ctx.Height() { a.dir.Y = -1 a.updown = 0 } else if a.position.Y-a.height < 0 { a.updown = 1 a.dir.Y = 1 } if a.position.X+a.width > a.ctx.Width() { a.dir.X = -1 } else if a.position.X-a.width < 0 { a.dir.X = 1 } }