diff --git a/animateDoorbell.go b/animateDoorbell.go new file mode 100644 index 0000000..9cdce38 --- /dev/null +++ b/animateDoorbell.go @@ -0,0 +1,23 @@ +package main + +import ( + "bytes" + b64 "encoding/base64" + "image" + + "github.com/disintegration/imaging" +) + +type Doorbell struct { + image image.Image +} + +func (a *Animation) clearDoorbell() { + var door Doorbell + a.doorbell = door +} +func loadImage(imgType string, img string, a *Animation) { + baseImage, _ := b64.StdEncoding.DecodeString(img) + bigImage, _, _ := image.Decode(bytes.NewReader(baseImage)) + a.doorbell.image = imaging.Resize(bigImage, 64, 64, imaging.Lanczos) +} diff --git a/animation.go b/animation.go deleted file mode 100644 index ef014ff..0000000 --- a/animation.go +++ /dev/null @@ -1,150 +0,0 @@ -package main - -import ( - "bytes" - b64 "encoding/base64" - "encoding/json" - "fmt" - "image" - "image/color" - "log" - "os" - "time" - - rgbmatrix "gitea.wagshome.duckdns.org/publicWagsHome/go-rpi-rgb-led-matrix" - "github.com/disintegration/imaging" - mqtt "github.com/eclipse/paho.mqtt.golang" - "github.com/fogleman/gg" -) - -// contents of struct mostly don't matter for toolkit. -type incomingMessage struct { - Type string `json:"type"` - Image string `json:"image"` -} - -type Animation struct { - ctx *gg.Context - position image.Point - dir image.Point - height int - width int - stroke int - images map[string]image.Image - updown string - mqmsg chan mqtt.Message - msg string - - countDown int -} - -// animator is a wrapping function for go routine that can receive an mq channel -func animator(tk *rgbmatrix.ToolKit, mqMessages chan mqtt.Message) { - //Playanimation comes from the toolkit, all it takes is an animation struct - tk.PlayAnimation(NewAnimation(image.Point{127, 64}, mqMessages)) -} - -func loadMario(file string) image.Image { - - reader, err := os.Open(file) - if err != nil { - log.Fatal(err) - } - rawMario, _, err := image.Decode(reader) - if err != nil { - log.Fatal(err) - } - mario := imaging.Resize(rawMario, 16, 16, imaging.Lanczos) - return mario -} - -func initialMap() map[string]image.Image { - imageMap := make(map[string]image.Image) - imageMap["marioUp"] = loadMario("marioUp.png") - imageMap["marioDown"] = loadMario("marioDown.png") - return imageMap -} - -// initializes the struct for the an play animation function, this could all be dumped into function that's wrapping go routine if I wanted -func NewAnimation(sz image.Point, mqMessages chan mqtt.Message) *Animation { - return &Animation{ - ctx: gg.NewContext(sz.X, sz.Y), - dir: image.Point{1, 1}, - height: 8, - width: 8, - stroke: 8, - images: initialMap(), - updown: "marioUp", - mqmsg: mqMessages, - countDown: 5000, - } -} - -func loadImage(imgType string, img string, a *Animation) { - baseImage, _ := b64.StdEncoding.DecodeString(img) - bigImage, _, _ := image.Decode(bytes.NewReader(baseImage)) - a.images[imgType] = imaging.Resize(bigImage, 64, 64, imaging.Lanczos) -} - -// what happens each frame, at an interval of 50 milliseconds -func (a *Animation) Next() (image.Image, <-chan time.Time, error) { - var incoming incomingMessage - - if a.countDown == 5000 { - a.animateMario() - } - if a.images["doorbell"] != nil { - if a.countDown > 0 { - a.ctx.DrawImageAnchored(a.images["doorbell"], 0, 0, 0, 0) - a.countDown -= 50 - } else { - //a.image = a.image[:len(a.image)-1] - delete(a.images, "doorbell") - a.countDown = 5000 - } - } - a.ctx.SetColor(color.White) - select { - case msg := <-a.mqmsg: - json.Unmarshal([]byte(string(msg.Payload())), &incoming) - fmt.Println(incoming.Type) - if incoming.Type == "doorbell" { - go loadImage(incoming.Type, incoming.Image, a) - } else { - a.ctx.DrawString(a.msg, 5, 9) - } - default: - } - return a.ctx.Image(), time.After(time.Millisecond * 50), nil -} - -func (a *Animation) animateMario() { - defer a.updateMarioPosition() - a.ctx.SetColor(color.Black) - a.ctx.Clear() - if a.dir.X == 1 { - a.ctx.DrawImageAnchored(a.images[a.updown], a.position.X, a.position.Y, 0.5, 0.5) - } else { - a.ctx.DrawImageAnchored(imaging.FlipH(a.images[a.updown]), a.position.X, a.position.Y, 0.5, 0.5) - } -} - -// what mario does every frame -func (a *Animation) updateMarioPosition() { - 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 = "marioUp" - } else if a.position.Y-a.height < 0 { - a.updown = "marioDown" - 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 - } -} diff --git a/animationMario.go b/animationMario.go new file mode 100644 index 0000000..2e83b00 --- /dev/null +++ b/animationMario.go @@ -0,0 +1,82 @@ +package main + +import ( + "image" + "image/color" + "log" + "os" + + "github.com/disintegration/imaging" +) + +type Mario struct { + position image.Point + dir image.Point + images map[string]image.Image + updown string +} + +// animator is a wrapping function for go routine that can receive an mq channel +func initialMario() Mario { + mario := Mario{ + images: initialMap(), + updown: "marioUp", + dir: image.Point{1, 1}, + } + return mario +} +func loadMario(file string) image.Image { + + reader, err := os.Open(file) + if err != nil { + log.Fatal(err) + } + rawMario, _, err := image.Decode(reader) + if err != nil { + log.Fatal(err) + } + mario := imaging.Resize(rawMario, 16, 16, imaging.Lanczos) + return mario +} + +func initialMap() map[string]image.Image { + imageMap := make(map[string]image.Image) + imageMap["marioUp"] = loadMario("marioUp.png") + imageMap["marioDown"] = loadMario("marioDown.png") + return imageMap +} + +// initializes the struct for the an play animation function, this could all be dumped into function that's wrapping go routine if I wanted + +// what happens each frame, at an interval of 50 milliseconds + +func (a *Animation) animateMario() { + defer a.updateMarioPosition() + a.ctx.SetColor(color.Black) + a.ctx.Clear() + if a.dir.X == 1 { + a.ctx.DrawImageAnchored(a.mario.images[a.mario.updown], a.mario.position.X, a.mario.position.Y, 0.5, 0.5) + } else { + a.ctx.DrawImageAnchored(imaging.FlipH(a.mario.images[a.mario.updown]), a.mario.position.X, a.mario.position.Y, 0.5, 0.5) + } +} + +// what mario does every frame +func (a *Animation) updateMarioPosition() { + a.mario.position.X += 1 * a.mario.dir.X + a.mario.position.Y += 1 * a.mario.dir.Y + + if a.mario.position.Y+a.height > a.ctx.Height() { + a.mario.dir.Y = -1 + a.mario.updown = "marioUp" + } else if a.position.Y-a.height < 0 { + a.mario.updown = "marioDown" + a.mario.dir.Y = 1 + } + + if a.mario.position.X+a.width > a.ctx.Width() { + a.mario.dir.X = -1 + } else if a.mario.position.X-a.width < 0 { + a.mario.dir.X = 1 + } +} diff --git a/board.go b/board.go new file mode 100644 index 0000000..a454e82 --- /dev/null +++ b/board.go @@ -0,0 +1,79 @@ +package main + +import ( + "encoding/json" + "fmt" + "image" + "image/color" + "time" + + rgbmatrix "gitea.wagshome.duckdns.org/publicWagsHome/go-rpi-rgb-led-matrix" + 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 + updown string + mqmsg chan mqtt.Message + msg string + mario Mario + countDown int + doorbell Doorbell +} + +func orchestrator(tk *rgbmatrix.ToolKit, mqMessages chan mqtt.Message) { + //Playanimation comes from the toolkit, all it takes is an animation struct + tk.PlayAnimation(animate(image.Point{127, 64}, mqMessages)) +} + +func animate(sz image.Point, mqMessages chan mqtt.Message) *Animation { + return &Animation{ + ctx: gg.NewContext(sz.X, sz.Y), + dir: image.Point{1, 1}, + height: 8, + width: 8, + stroke: 8, + updown: "marioUp", + mqmsg: mqMessages, + countDown: 5000, + mario: Mario{ + images: initialMap(), + updown: "marioUp", + dir: image.Point{1, 1}, + }, + } +} +func (a *Animation) Next() (image.Image, <-chan time.Time, error) { + var incoming incomingMessage + + a.animateMario() + if a.doorbell != (Doorbell{}) { + if a.countDown > 0 { + a.ctx.DrawImageAnchored(a.doorbell.image, 0, 0, 0, 0) + a.countDown -= 50 + } else { + //a.image = a.image[:len(a.image)-1] + a.doorbell = Doorbell{} + a.countDown = 5000 + } + } + a.ctx.SetColor(color.White) + select { + case msg := <-a.mqmsg: + json.Unmarshal([]byte(string(msg.Payload())), &incoming) + fmt.Println(incoming.Type) + if incoming.Type == "doorbell" { + go loadImage(incoming.Type, incoming.Image, a) + } else { + a.ctx.DrawString(a.msg, 5, 9) + } + default: + } + return a.ctx.Image(), time.After(time.Millisecond * 50), nil +} diff --git a/main.go b/main.go index 0bde2fc..76b4c13 100644 --- a/main.go +++ b/main.go @@ -62,7 +62,7 @@ func main() { tk := rgbmatrix.NewToolKit(m) defer tk.Close() log.Println("making animator") - go animator(tk, mqMessages) + go orchestrator(tk, mqMessages) log.Println("I guess I'm at the end") sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) diff --git a/mqttcoms.go b/mqttcoms.go index 6263cf0..e13ff53 100644 --- a/mqttcoms.go +++ b/mqttcoms.go @@ -9,6 +9,12 @@ import ( mqtt "github.com/eclipse/paho.mqtt.golang" ) +// contents of struct mostly don't matter for toolkit. +type incomingMessage struct { + Type string `json:"type"` + Image string `json:"image"` +} + // listens on topic for messages func listener(mqMessages chan mqtt.Message) { opts := setupMQTT()