diff --git a/Makefile b/Makefile index 88b7d76..69a13b0 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,10 @@ setup: - cd go-rpi-rgb-led-matrix/lib/rpi-rgb-led-matrix + git clone https://github.com/RockKeeper/go-rpi-rgb-led-matrix + rm go-rpi-rgb-led-matrix/go.mod go-rpi-rgb-led-matrix/go.sum + sed -i 's|github.com/RockKeeper|git.dvdt.dev/david/bitcoin-ticker-pi|g' + cd go-rpi-rgb-led-matrix/lib + git submodule update --init + cd rpi-rgb-led-matrix make run: diff --git a/go-rpi-rgb-led-matrix/.gitmodules b/go-rpi-rgb-led-matrix/.gitmodules deleted file mode 100644 index 492da44..0000000 --- a/go-rpi-rgb-led-matrix/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "lib/rpi-rgb-led-matrix"] - path = lib/rpi-rgb-led-matrix - url = https://github.com/hzeller/rpi-rgb-led-matrix.git diff --git a/go-rpi-rgb-led-matrix/.travis.yml b/go-rpi-rgb-led-matrix/.travis.yml deleted file mode 100644 index 378d24a..0000000 --- a/go-rpi-rgb-led-matrix/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: go - -go: - - 1.6 - - 1.7 - - tip - -before_install: - - cd $GOPATH/src/github.com/mcuadros/go-rpi-rgb-led-matrix/lib/rpi-rgb-led-matrix/ - - git submodule update --init - - make - - cd $GOPATH/src/github.com/mcuadros/go-rpi-rgb-led-matrix/ - - go get -t -v ./... - - go install -v ./... diff --git a/go-rpi-rgb-led-matrix/LICENSE b/go-rpi-rgb-led-matrix/LICENSE deleted file mode 100644 index 81e256d..0000000 --- a/go-rpi-rgb-led-matrix/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 Máximo Cuadros - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/go-rpi-rgb-led-matrix/README.md b/go-rpi-rgb-led-matrix/README.md deleted file mode 100644 index d387d73..0000000 --- a/go-rpi-rgb-led-matrix/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# go-rpi-rgb-led-matrix [![GoDoc](https://godoc.org/github.com/mcuadros/go-rpi-rgb-led-matrix?status.svg)](https://godoc.org/github.com/mcuadros/go-rpi-rgb-led-matrix) [![Build Status](https://travis-ci.org/mcuadros/go-rpi-rgb-led-matrix.svg?branch=master)](https://travis-ci.org/mcuadros/go-rpi-rgb-led-matrix) - - -Go binding for [`rpi-rgb-led-matrix`](https://github.com/hzeller/rpi-rgb-led-matrix) an excellent C++ library to control [RGB LED displays](https://learn.adafruit.com/32x16-32x32-rgb-led-matrix/overview) with Raspberry Pi GPIO. - -This library includes the basic bindings to control de LED Matrix directly and also a convenient [ToolKit](https://godoc.org/github.com/mcuadros/go-rpi-rgb-led-matrix#ToolKit) with more high level functions. Also some [examples](https://github.com/mcuadros/go-rpi-rgb-led-matrix/tree/master/examples) are included to test the library and the configuration. - -The [`Canvas`](https://godoc.org/github.com/mcuadros/go-rpi-rgb-led-matrix#Canvas) struct implements the [`image.Image`](https://golang.org/pkg/image/#Image) interface from the Go standard library. This makes the interaction with the matrix simple as work with a normal image in Go, allowing the usage of any Go library build around the `image.Image` interface. - -To learn about the configuration and the wiring go to the [original library](https://github.com/hzeller/rpi-rgb-led-matrix), is highly detailed and well explained. - -Installation ------------- - -The recommended way to install `go-rpi-rgb-led-matrix` is: - -```sh -go get github.com/mcuadros/go-rpi-rgb-led-matrix -``` - -Then you will get an **expected** error like this: - -``` -# github.com/mcuadros/go-rpi-rgb-led-matrix -/usr/bin/ld: cannot find -lrgbmatrix -collect2: error: ld returned 1 exit status -``` - -This happens because you need to compile the `rgbmatrix` C bindings: -```sh -cd $GOPATH/src/github.com/mcuadros/go-rpi-rgb-led-matrix/lib/rpi-rgb-led-matrix/ -git submodule update --init -make -cd $GOPATH/src/github.com/mcuadros/go-rpi-rgb-led-matrix/ -go install -v ./... -``` - -Examples --------- - -Setting all the pixels to white: - -```go -// create a new Matrix instance with the DefaultConfig -m, _ := rgbmatrix.NewRGBLedMatrix(&rgbmatrix.DefaultConfig) - -// create the Canvas, implements the image.Image interface -c := rgbmatrix.NewCanvas(m) -defer c.Close() // don't forgot close the Matrix, if not your leds will remain on - -// using the standard draw.Draw function we copy a white image onto the Canvas -draw.Draw(c, c.Bounds(), &image.Uniform{color.White}, image.ZP, draw.Src) - -// don't forget call Render to display the new led status -c.Render() -``` - -Playing a GIF into your matrix during 30 seconds: - -```go -// create a new Matrix instance with the DefaultConfig -m, _ := rgbmatrix.NewRGBLedMatrix(&rgbmatrix.DefaultConfig) - -// create a ToolKit instance -tk := rgbmatrix.NewToolKit(m) -defer tk.Close() // don't forgot close the Matrix, if not your leds will remain on - -// open the gif file for reading -file, _ := os.Open("mario.gif") - -// play of the gif using the io.Reader -close, _ := tk.PlayGIF(f) -fatal(err) - -// we wait 30 seconds and then we stop the playing gif sending a True to the returned chan -time.Sleep(time.Second * 30) -close <- true -``` - -The image of the header was recorded using this few lines, the running _Mario_ gif, and three 32x64 pannels. - - -Check the folder [`examples`](https://github.com/mcuadros/go-rpi-rgb-led-matrix/tree/master/examples) folder for more examples - - -Matrix Emulation ----------------- - -As part of the library an small Matrix emulator is provided. The emulator renderize a virtual RGB matrix on a window in your desktop, without needing a real RGB matrix connected to your computer. - -To execute the emulator set the `MATRIX_EMULATOR` environment variable to `1`, then when `NewRGBLedMatrix` is used, a `emulator.Emulator` is returned instead of a interface the real board. - - -License -------- - -MIT, see [LICENSE](LICENSE) diff --git a/go-rpi-rgb-led-matrix/canvas.go b/go-rpi-rgb-led-matrix/canvas.go deleted file mode 100644 index 3b7d7f5..0000000 --- a/go-rpi-rgb-led-matrix/canvas.go +++ /dev/null @@ -1,77 +0,0 @@ -package rgbmatrix - -import ( - "image" - "image/color" - "image/draw" -) - -// Canvas is a image.Image representation of a WS281x matrix, it implements -// image.Image interface and can be used with draw.Draw for example -type Canvas struct { - w, h int - m Matrix - closed bool -} - -// NewCanvas returns a new Canvas using the given width and height and creates -// a new WS281x matrix using the given config -func NewCanvas(m Matrix) *Canvas { - w, h := m.Geometry() - return &Canvas{ - w: w, - h: h, - m: m, - } -} - -// Render update the display with the data from the LED buffer -func (c *Canvas) Render() error { - return c.m.Render() -} - -// ColorModel returns the canvas' color model, always color.RGBAModel -func (c *Canvas) ColorModel() color.Model { - return color.RGBAModel -} - -// Bounds return the topology of the Canvas -func (c *Canvas) Bounds() image.Rectangle { - return image.Rect(0, 0, c.w, c.h) -} - -// At returns the color of the pixel at (x, y) -func (c *Canvas) At(x, y int) color.Color { - return c.m.At(c.position(x, y)) -} - -// Set set LED at position x,y to the provided 24-bit color value -func (c *Canvas) Set(x, y int, color color.Color) { - c.m.Set(c.position(x, y), color) -} - -func (c *Canvas) position(x, y int) int { - return x + (y * c.w) -} - -// Clear set all the leds on the matrix with color.Black -func (c *Canvas) Clear() error { - draw.Draw(c, c.Bounds(), &image.Uniform{color.Black}, image.ZP, draw.Src) - return c.m.Render() -} - -// Close clears the matrix and close the matrix -func (c *Canvas) Close() error { - c.Clear() - return c.m.Close() -} - -// Matrix is an interface that represent any RGB matrix, very useful for testing -type Matrix interface { - Geometry() (width, height int) - At(position int) color.Color - Set(position int, c color.Color) - Apply([]color.Color) error - Render() error - Close() error -} diff --git a/go-rpi-rgb-led-matrix/canvas_test.go b/go-rpi-rgb-led-matrix/canvas_test.go deleted file mode 100644 index 3bb1972..0000000 --- a/go-rpi-rgb-led-matrix/canvas_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package rgbmatrix - -import ( - "image/color" - "testing" - - . "gopkg.in/check.v1" -) - -func Test(t *testing.T) { TestingT(t) } - -type CanvasSuite struct{} - -var _ = Suite(&CanvasSuite{}) - -func (s *CanvasSuite) TestNewCanvas(c *C) { - canvas := NewCanvas(NewMatrixMock()) - c.Assert(canvas, NotNil) - c.Assert(canvas.w, Equals, 64) - c.Assert(canvas.h, Equals, 32) -} - -func (s *CanvasSuite) TestRender(c *C) { - m := NewMatrixMock() - canvas := &Canvas{m: m} - canvas.Render() - - c.Assert(m.called["Render"], Equals, true) -} - -func (s *CanvasSuite) TestColorModel(c *C) { - canvas := &Canvas{} - - c.Assert(canvas.ColorModel(), Equals, color.RGBAModel) -} - -func (s *CanvasSuite) TestBounds(c *C) { - - canvas := &Canvas{w: 10, h: 20} - - b := canvas.Bounds() - c.Assert(b.Min.X, Equals, 0) - c.Assert(b.Min.Y, Equals, 0) - c.Assert(b.Max.X, Equals, 10) - c.Assert(b.Max.Y, Equals, 20) -} - -func (s *CanvasSuite) TestAt(c *C) { - m := NewMatrixMock() - canvas := &Canvas{w: 10, h: 20, m: m} - canvas.At(5, 15) - - c.Assert(m.called["At"], Equals, 155) -} - -func (s *CanvasSuite) TestSet(c *C) { - m := NewMatrixMock() - canvas := &Canvas{w: 10, h: 20, m: m} - canvas.Set(5, 15, color.White) - - c.Assert(m.called["Set"], Equals, 155) - c.Assert(m.colors[155], Equals, color.White) -} - -func (s *CanvasSuite) TestClear(c *C) { - m := NewMatrixMock() - - canvas := &Canvas{w: 10, h: 20, m: m} - err := canvas.Clear() - c.Assert(err, IsNil) - - for _, px := range m.colors { - c.Assert(px, Equals, color.Black) - } - - c.Assert(m.called["Render"], Equals, true) -} - -func (s *CanvasSuite) TestClose(c *C) { - m := NewMatrixMock() - canvas := &Canvas{w: 10, h: 20, m: m} - err := canvas.Close() - c.Assert(err, IsNil) - - for _, px := range m.colors { - c.Assert(px, Equals, color.Black) - } - - c.Assert(m.called["Render"], Equals, true) -} - -type MatrixMock struct { - called map[string]interface{} - colors []color.Color -} - -func NewMatrixMock() *MatrixMock { - return &MatrixMock{ - called: make(map[string]interface{}, 0), - colors: make([]color.Color, 200), - } -} - -func (m *MatrixMock) Geometry() (width, height int) { - return 64, 32 -} - -func (m *MatrixMock) Initialize() error { - m.called["Initialize"] = true - return nil -} - -func (m *MatrixMock) At(position int) color.Color { - m.called["At"] = position - return color.Black -} - -func (m *MatrixMock) Set(position int, c color.Color) { - m.called["Set"] = position - m.colors[position] = c -} - -func (m *MatrixMock) Apply(leds []color.Color) error { - for position, l := range leds { - m.Set(position, l) - } - - return m.Render() -} - -func (m *MatrixMock) Render() error { - m.called["Render"] = true - return nil -} - -func (m *MatrixMock) Close() error { - m.called["Close"] = true - return nil -} diff --git a/go-rpi-rgb-led-matrix/emulator/emulator.go b/go-rpi-rgb-led-matrix/emulator/emulator.go deleted file mode 100644 index 60ab8be..0000000 --- a/go-rpi-rgb-led-matrix/emulator/emulator.go +++ /dev/null @@ -1,204 +0,0 @@ -package emulator - -import ( - "fmt" - "image" - "image/color" - "os" - "sync" - - "golang.org/x/exp/shiny/driver" - "golang.org/x/exp/shiny/screen" - "golang.org/x/mobile/event/paint" - "golang.org/x/mobile/event/size" -) - -const DefaultPixelPitch = 12 -const windowTitle = "RGB led matrix emulator" - -type Emulator struct { - PixelPitch int - Gutter int - Width int - Height int - GutterColor color.Color - PixelPitchToGutterRatio int - Margin int - - leds []color.Color - w screen.Window - s screen.Screen - wg sync.WaitGroup - - isReady bool -} - -func NewEmulator(w, h, pixelPitch int, autoInit bool) *Emulator { - e := &Emulator{ - Width: w, - Height: h, - GutterColor: color.Gray{Y: 20}, - PixelPitchToGutterRatio: 2, - Margin: 10, - } - e.updatePixelPitchForGutter(pixelPitch / e.PixelPitchToGutterRatio) - - if autoInit { - e.Init() - } - - return e -} - -// Init initialize the emulator, creating a new Window and waiting until is -// painted. If something goes wrong the function panics -func (e *Emulator) Init() { - e.leds = make([]color.Color, e.Width*e.Height) - - e.wg.Add(1) - go driver.Main(e.mainWindowLoop) - e.wg.Wait() -} - -func (e *Emulator) mainWindowLoop(s screen.Screen) { - var err error - e.s = s - // Calculate initial window size based on whatever our gutter/pixel pitch currently is. - dims := e.matrixWithMarginsRect() - e.w, err = s.NewWindow(&screen.NewWindowOptions{ - Title: windowTitle, - Width: dims.Max.X, - Height: dims.Max.Y, - }) - - if err != nil { - panic(err) - } - - defer e.w.Release() - - var sz size.Event - for { - evn := e.w.NextEvent() - switch evn := evn.(type) { - case paint.Event: - e.drawContext(sz) - if e.isReady { - continue - } - - e.Apply(make([]color.Color, e.Width*e.Height)) - e.wg.Done() - e.isReady = true - case size.Event: - sz = evn - - case error: - fmt.Fprintln(os.Stderr, e) - } - } -} - -func (e *Emulator) drawContext(sz size.Event) { - e.updatePixelPitchForGutter(e.calculateGutterForViewableArea(sz.Size())) - // Fill entire background with white. - e.w.Fill(sz.Bounds(), color.White, screen.Src) - // Fill matrix display rectangle with the gutter color. - e.w.Fill(e.matrixWithMarginsRect(), e.GutterColor, screen.Src) - // Set all LEDs to black. - e.Apply(make([]color.Color, e.Width*e.Height)) -} - -// Some formulas that allowed me to better understand the drawable area. I found that the math was -// easiest when put in terms of the Gutter width, hence the addition of PixelPitchToGutterRatio. -// -// PixelPitch = PixelPitchToGutterRatio * Gutter -// DisplayWidth = (PixelPitch * LEDColumns) + (Gutter * (LEDColumns - 1)) + (2 * Margin) -// Gutter = (DisplayWidth - (2 * Margin)) / (PixelPitchToGutterRatio * LEDColumns + LEDColumns - 1) -// -// MMMMMMMMMMMMMMMM.....MMMM -// MGGGGGGGGGGGGGGG.....GGGM -// MGLGLGLGLGLGLGLG.....GLGM -// MGGGGGGGGGGGGGGG.....GGGM -// MGLGLGLGLGLGLGLG.....GLGM -// MGGGGGGGGGGGGGGG.....GGGM -// ......................... -// MGGGGGGGGGGGGGGG.....GGGM -// MGLGLGLGLGLGLGLG.....GLGM -// MGGGGGGGGGGGGGGG.....GGGM -// MMMMMMMMMMMMMMMM.....MMMM -// -// where: -// M = Margin -// G = Gutter -// L = LED - -// matrixWithMarginsRect Returns a Rectangle that describes entire emulated RGB Matrix, including margins. -func (e *Emulator) matrixWithMarginsRect() image.Rectangle { - upperLeftLED := e.ledRect(0, 0) - lowerRightLED := e.ledRect(e.Width-1, e.Height-1) - return image.Rect(upperLeftLED.Min.X-e.Margin, upperLeftLED.Min.Y-e.Margin, lowerRightLED.Max.X+e.Margin, lowerRightLED.Max.Y+e.Margin) -} - -// ledRect Returns a Rectangle for the LED at col and row. -func (e *Emulator) ledRect(col int, row int) image.Rectangle { - x := (col * (e.PixelPitch + e.Gutter)) + e.Margin - y := (row * (e.PixelPitch + e.Gutter)) + e.Margin - return image.Rect(x, y, x+e.PixelPitch, y+e.PixelPitch) -} - -// calculateGutterForViewableArea As the name states, calculates the size of the gutter for a given viewable area. -// It's easier to understand the geometry of the matrix on screen when put in terms of the gutter, -// hence the shift toward calculating the gutter size. -func (e *Emulator) calculateGutterForViewableArea(size image.Point) int { - maxGutterInX := (size.X - 2*e.Margin) / (e.PixelPitchToGutterRatio*e.Width + e.Width - 1) - maxGutterInY := (size.Y - 2*e.Margin) / (e.PixelPitchToGutterRatio*e.Height + e.Height - 1) - if maxGutterInX < maxGutterInY { - return maxGutterInX - } - return maxGutterInY -} - -func (e *Emulator) updatePixelPitchForGutter(gutterWidth int) { - e.PixelPitch = e.PixelPitchToGutterRatio * gutterWidth - e.Gutter = gutterWidth -} - -func (e *Emulator) Geometry() (width, height int) { - return e.Width, e.Height -} - -func (e *Emulator) Apply(leds []color.Color) error { - defer func() { e.leds = make([]color.Color, e.Height*e.Width) }() - - var c color.Color - for col := 0; col < e.Width; col++ { - for row := 0; row < e.Height; row++ { - c = e.At(col + (row * e.Width)) - e.w.Fill(e.ledRect(col, row), c, screen.Over) - } - } - - e.w.Publish() - return nil -} - -func (e *Emulator) Render() error { - return e.Apply(e.leds) -} - -func (e *Emulator) At(position int) color.Color { - if e.leds[position] == nil { - return color.Black - } - - return e.leds[position] -} - -func (e *Emulator) Set(position int, c color.Color) { - e.leds[position] = color.RGBAModel.Convert(c) -} - -func (e *Emulator) Close() error { - return nil -} diff --git a/go-rpi-rgb-led-matrix/examples/animation/main.go b/go-rpi-rgb-led-matrix/examples/animation/main.go deleted file mode 100644 index aa5a1b9..0000000 --- a/go-rpi-rgb-led-matrix/examples/animation/main.go +++ /dev/null @@ -1,98 +0,0 @@ -package main - -import ( - "flag" - "image" - "image/color" - "time" - - "github.com/RockKeeper/go-rpi-rgb-led-matrix" - "github.com/fogleman/gg" -) - -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 - - m, err := rgbmatrix.NewRGBLedMatrix(config) - fatal(err) - - tk := rgbmatrix.NewToolKit(m) - defer tk.Close() - - tk.PlayAnimation(NewAnimation(image.Point{64, 32})) -} - -func init() { - flag.Parse() -} - -func fatal(err error) { - if err != nil { - panic(err) - } -} - -type Animation struct { - ctx *gg.Context - position image.Point - dir image.Point - stroke int -} - -func NewAnimation(sz image.Point) *Animation { - return &Animation{ - ctx: gg.NewContext(sz.X, sz.Y), - dir: image.Point{1, 1}, - stroke: 5, - } -} - -func (a *Animation) Next() (image.Image, <-chan time.Time, error) { - defer a.updatePosition() - - a.ctx.SetColor(color.Black) - a.ctx.Clear() - - a.ctx.DrawCircle(float64(a.position.X), float64(a.position.Y), float64(a.stroke)) - a.ctx.SetColor(color.RGBA{255, 0, 0, 255}) - a.ctx.Fill() - 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.stroke > a.ctx.Height() { - a.dir.Y = -1 - } else if a.position.Y-a.stroke < 0 { - a.dir.Y = 1 - } - - if a.position.X+a.stroke > a.ctx.Width() { - a.dir.X = -1 - } else if a.position.X-a.stroke < 0 { - a.dir.X = 1 - } -} diff --git a/go-rpi-rgb-led-matrix/examples/basic/main.go b/go-rpi-rgb-led-matrix/examples/basic/main.go deleted file mode 100644 index bdc01a8..0000000 --- a/go-rpi-rgb-led-matrix/examples/basic/main.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "image/color" - - "github.com/RockKeeper/go-rpi-rgb-led-matrix" -) - -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 - - m, err := rgbmatrix.NewRGBLedMatrix(config) - fatal(err) - - c := rgbmatrix.NewCanvas(m) - defer c.Close() - - bounds := c.Bounds() - for x := bounds.Min.X; x < bounds.Max.X; x++ { - for y := bounds.Min.Y; y < bounds.Max.Y; y++ { - fmt.Println("x", x, "y", y) - c.Set(x, y, color.RGBA{255, 0, 0, 255}) - c.Render() - } - } -} - -func init() { - flag.Parse() -} - -func fatal(err error) { - if err != nil { - panic(err) - } -} diff --git a/go-rpi-rgb-led-matrix/examples/image/main.go b/go-rpi-rgb-led-matrix/examples/image/main.go deleted file mode 100644 index 6f73631..0000000 --- a/go-rpi-rgb-led-matrix/examples/image/main.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - "flag" - "os" - "time" - - "github.com/RockKeeper/go-rpi-rgb-led-matrix" - "github.com/disintegration/imaging" -) - -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.") - img = flag.String("image", "", "image path") - - rotate = flag.Int("rotate", 0, "rotate angle, 90, 180, 270") -) - -func main() { - f, err := os.Open(*img) - fatal(err) - - 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 - - m, err := rgbmatrix.NewRGBLedMatrix(config) - fatal(err) - - tk := rgbmatrix.NewToolKit(m) - defer tk.Close() - - switch *rotate { - case 90: - tk.Transform = imaging.Rotate90 - case 180: - tk.Transform = imaging.Rotate180 - case 270: - tk.Transform = imaging.Rotate270 - } - - close, err := tk.PlayGIF(f) - fatal(err) - - time.Sleep(time.Second * 30) - close <- true -} - -func init() { - flag.Parse() -} - -func fatal(err error) { - if err != nil { - panic(err) - } -} diff --git a/go-rpi-rgb-led-matrix/examples/rpc/client/main.go b/go-rpi-rgb-led-matrix/examples/rpc/client/main.go deleted file mode 100644 index bcfe434..0000000 --- a/go-rpi-rgb-led-matrix/examples/rpc/client/main.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "flag" - "os" - "time" - - "github.com/RockKeeper/go-rpi-rgb-led-matrix" - "github.com/RockKeeper/go-rpi-rgb-led-matrix/rpc" -) - -var ( - img = flag.String("image", "", "image path") -) - -func main() { - f, err := os.Open(*img) - fatal(err) - - m, err := rpc.NewClient("tcp", "10.20.20.20:1234") - fatal(err) - - tk := rgbmatrix.NewToolKit(m) - close, err := tk.PlayGIF(f) - fatal(err) - - time.Sleep(time.Second * 3) - close <- true -} - -func init() { - flag.Parse() -} - -func fatal(err error) { - if err != nil { - panic(err) - } -} diff --git a/go-rpi-rgb-led-matrix/examples/rpc/server/main.go b/go-rpi-rgb-led-matrix/examples/rpc/server/main.go deleted file mode 100644 index 338d036..0000000 --- a/go-rpi-rgb-led-matrix/examples/rpc/server/main.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "flag" - - "github.com/RockKeeper/go-rpi-rgb-led-matrix" - "github.com/RockKeeper/go-rpi-rgb-led-matrix/rpc" -) - -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 - - m, err := rgbmatrix.NewRGBLedMatrix(config) - fatal(err) - - rpc.Serve(m) -} - -func fatal(err error) { - if err != nil { - panic(err) - } -} diff --git a/go-rpi-rgb-led-matrix/matrix.go b/go-rpi-rgb-led-matrix/matrix.go deleted file mode 100644 index 7c0a2f9..0000000 --- a/go-rpi-rgb-led-matrix/matrix.go +++ /dev/null @@ -1,274 +0,0 @@ -package rgbmatrix - -/* -#cgo CFLAGS: -std=c99 -I${SRCDIR}/lib/rpi-rgb-led-matrix/include -DSHOW_REFRESH_RATE -#cgo LDFLAGS: -lrgbmatrix -L${SRCDIR}/lib/rpi-rgb-led-matrix/lib -lstdc++ -lm -#include - -void led_matrix_swap(struct RGBLedMatrix *matrix, struct LedCanvas *offscreen_canvas, - int width, int height, const uint32_t pixels[]) { - - - int i, x, y; - uint32_t color; - for (x = 0; x < width; ++x) { - for (y = 0; y < height; ++y) { - i = x + (y * width); - color = pixels[i]; - - led_canvas_set_pixel(offscreen_canvas, x, y, - (color >> 16) & 255, (color >> 8) & 255, color & 255); - } - } - - offscreen_canvas = led_matrix_swap_on_vsync(matrix, offscreen_canvas); -} - -void set_show_refresh_rate(struct RGBLedMatrixOptions *o, int show_refresh_rate) { - o->show_refresh_rate = show_refresh_rate != 0 ? 1 : 0; -} - -void set_disable_hardware_pulsing(struct RGBLedMatrixOptions *o, int disable_hardware_pulsing) { - o->disable_hardware_pulsing = disable_hardware_pulsing != 0 ? 1 : 0; -} - -void set_inverse_colors(struct RGBLedMatrixOptions *o, int inverse_colors) { - o->inverse_colors = inverse_colors != 0 ? 1 : 0; -} -*/ -import "C" -import ( - "fmt" - "image/color" - "os" - "unsafe" - - "git.dvdt.dev/david/bitcoin-ticker-pi/go-rpi-rgb-led-matrix/emulator" -) - -// DefaultConfig default WS281x configuration -var DefaultConfig = HardwareConfig{ - Rows: 32, - Cols: 32, - ChainLength: 1, - Parallel: 1, - PWMBits: 11, - PWMLSBNanoseconds: 130, - Brightness: 100, - ScanMode: Progressive, -} - -// HardwareConfig rgb-led-matrix configuration -type HardwareConfig struct { - // Rows the number of rows supported by the display, so 32 or 16. - Rows int - // Cols the number of columns supported by the display, so 32 or 64 . - Cols int - // ChainLengthis the number of displays daisy-chained together - // (output of one connected to input of next). - ChainLength int - // Parallel is the number of parallel chains connected to the Pi; in old Pis - // with 26 GPIO pins, that is 1, in newer Pis with 40 interfaces pins, that - // can also be 2 or 3. The effective number of pixels in vertical direction is - // then thus rows * parallel. - Parallel int - // Set PWM bits used for output. Default is 11, but if you only deal with - // limited comic-colors, 1 might be sufficient. Lower require less CPU and - // increases refresh-rate. - PWMBits int - // Change the base time-unit for the on-time in the lowest significant bit in - // nanoseconds. Higher numbers provide better quality (more accurate color, - // less ghosting), but have a negative impact on the frame rate. - PWMLSBNanoseconds int // the DMA channel to use - // Brightness is the initial brightness of the panel in percent. Valid range - // is 1..100 - Brightness int - // ScanMode progressive or interlaced - ScanMode ScanMode // strip color layout - // Disable the PWM hardware subsystem to create pulses. Typically, you don't - // want to disable hardware pulsing, this is mostly for debugging and figuring - // out if there is interference with the sound system. - // This won't do anything if output enable is not connected to GPIO 18 in - // non-standard wirings. - DisableHardwarePulsing bool - - ShowRefreshRate bool - InverseColors bool - - // Name of GPIO mapping used - HardwareMapping string -} - -func (c *HardwareConfig) geometry() (width, height int) { - return c.Cols * c.ChainLength, c.Rows * c.Parallel -} - -func (c *HardwareConfig) toC() *C.struct_RGBLedMatrixOptions { - o := &C.struct_RGBLedMatrixOptions{} - o.rows = C.int(c.Rows) - o.cols = C.int(c.Cols) - o.chain_length = C.int(c.ChainLength) - o.parallel = C.int(c.Parallel) - o.pwm_bits = C.int(c.PWMBits) - o.pwm_lsb_nanoseconds = C.int(c.PWMLSBNanoseconds) - o.brightness = C.int(c.Brightness) - o.scan_mode = C.int(c.ScanMode) - o.hardware_mapping = C.CString(c.HardwareMapping) - - if c.ShowRefreshRate == true { - C.set_show_refresh_rate(o, C.int(1)) - } else { - C.set_show_refresh_rate(o, C.int(0)) - } - - if c.DisableHardwarePulsing == true { - C.set_disable_hardware_pulsing(o, C.int(1)) - } else { - C.set_disable_hardware_pulsing(o, C.int(0)) - } - - if c.InverseColors == true { - C.set_inverse_colors(o, C.int(1)) - } else { - C.set_inverse_colors(o, C.int(0)) - } - - return o -} - -type ScanMode int8 - -const ( - Progressive ScanMode = 0 - Interlaced ScanMode = 1 -) - -// RGBLedMatrix matrix representation for ws281x -type RGBLedMatrix struct { - Config *HardwareConfig - - height int - width int - matrix *C.struct_RGBLedMatrix - buffer *C.struct_LedCanvas - leds []C.uint32_t -} - -const MatrixEmulatorENV = "MATRIX_EMULATOR" - -// NewRGBLedMatrix returns a new matrix using the given size and config -func NewRGBLedMatrix(config *HardwareConfig) (c Matrix, err error) { - defer func() { - if r := recover(); r != nil { - var ok bool - err, ok = r.(error) - if !ok { - err = fmt.Errorf("error creating matrix: %v", r) - } - } - }() - - if isMatrixEmulator() { - return buildMatrixEmulator(config), nil - } - - w, h := config.geometry() - m := C.led_matrix_create_from_options(config.toC(), nil, nil) - b := C.led_matrix_create_offscreen_canvas(m) - c = &RGBLedMatrix{ - Config: config, - width: w, height: h, - matrix: m, - buffer: b, - leds: make([]C.uint32_t, w*h), - } - if m == nil { - return nil, fmt.Errorf("unable to allocate memory") - } - - return c, nil -} - -func isMatrixEmulator() bool { - if os.Getenv(MatrixEmulatorENV) == "1" { - return true - } - - return false -} - -func buildMatrixEmulator(config *HardwareConfig) Matrix { - w, h := config.geometry() - return emulator.NewEmulator(w, h, emulator.DefaultPixelPitch, true) -} - -// Initialize initialize library, must be called once before other functions are -// called. -func (c *RGBLedMatrix) Initialize() error { - return nil -} - -// Geometry returns the width and the height of the matrix -func (c *RGBLedMatrix) Geometry() (width, height int) { - return c.width, c.height -} - -// Apply set all the pixels to the values contained in leds -func (c *RGBLedMatrix) Apply(leds []color.Color) error { - for position, l := range leds { - c.Set(position, l) - } - - return c.Render() -} - -// Render update the display with the data from the LED buffer -func (c *RGBLedMatrix) Render() error { - w, h := c.Config.geometry() - - C.led_matrix_swap( - c.matrix, - c.buffer, - C.int(w), C.int(h), - (*C.uint32_t)(unsafe.Pointer(&c.leds[0])), - ) - - c.leds = make([]C.uint32_t, w*h) - return nil -} - -// At return an Color which allows access to the LED display data as -// if it were a sequence of 24-bit RGB values. -func (c *RGBLedMatrix) At(position int) color.Color { - return uint32ToColor(c.leds[position]) -} - -// Set set LED at position x,y to the provided 24-bit color value. -func (c *RGBLedMatrix) Set(position int, color color.Color) { - c.leds[position] = C.uint32_t(colorToUint32(color)) -} - -// Close finalizes the ws281x interface -func (c *RGBLedMatrix) Close() error { - C.led_matrix_delete(c.matrix) - return nil -} - -func colorToUint32(c color.Color) uint32 { - if c == nil { - return 0 - } - - // A color's RGBA method returns values in the range [0, 65535] - red, green, blue, _ := c.RGBA() - return (red>>8)<<16 | (green>>8)<<8 | blue>>8 -} - -func uint32ToColor(u C.uint32_t) color.Color { - return color.RGBA{ - uint8(u>>16) & 255, - uint8(u>>8) & 255, - uint8(u>>0) & 255, - 0, - } -} diff --git a/go-rpi-rgb-led-matrix/rpc/client.go b/go-rpi-rgb-led-matrix/rpc/client.go deleted file mode 100644 index 0016762..0000000 --- a/go-rpi-rgb-led-matrix/rpc/client.go +++ /dev/null @@ -1,79 +0,0 @@ -package rpc - -import ( - "encoding/gob" - "image/color" - "net/rpc" - - "github.com/RockKeeper/go-rpi-rgb-led-matrix" -) - -func init() { - gob.Register(color.RGBA{}) -} - -// RGBLedMatrix matrix representation for ws281x -type Client struct { - network string - addr string - client *rpc.Client - leds []color.Color -} - -// NewRGBLedMatrix returns a new matrix using the given size and config -func NewClient(network, addr string) (rgbmatrix.Matrix, error) { - client, err := rpc.DialHTTP(network, addr) - if err != nil { - return nil, err - } - - return &Client{ - network: network, - addr: addr, - client: client, - leds: make([]color.Color, 2048), - }, nil -} - -// Geometry returns the width and the height of the matrix -func (c *Client) Geometry() (width, height int) { - var reply *GeometryReply - err := c.client.Call("RPCMatrix.Geometry", &GeometryArgs{}, &reply) - if err != nil { - panic(err) - } - - return reply.Width, reply.Height -} - -func (c *Client) Apply(leds []color.Color) error { - defer func() { c.leds = make([]color.Color, 2048) }() - - var reply *ApplyReply - return c.client.Call("RPCMatrix.Apply", &ApplyArgs{Colors: leds}, &reply) -} - -// Render update the display with the data from the LED buffer -func (c *Client) Render() error { - return c.Apply(c.leds) -} - -// At return an Color which allows access to the LED display data as -// if it were a sequence of 24-bit RGB values. -func (c *Client) At(position int) color.Color { - if c.leds[position] == nil { - return color.Black - } - - return c.leds[position] -} - -// Set set LED at position x,y to the provided 24-bit color value. -func (m *Client) Set(position int, c color.Color) { - m.leds[position] = color.RGBAModel.Convert(c) -} - -// Close finalizes the ws281x interface -func (c *Client) Close() error { - return c.Apply(make([]color.Color, 2048)) -} diff --git a/go-rpi-rgb-led-matrix/rpc/server.go b/go-rpi-rgb-led-matrix/rpc/server.go deleted file mode 100644 index b98b190..0000000 --- a/go-rpi-rgb-led-matrix/rpc/server.go +++ /dev/null @@ -1,54 +0,0 @@ -package rpc - -import ( - "fmt" - "image/color" - "log" - "net" - "net/http" - "net/rpc" - - "github.com/RockKeeper/go-rpi-rgb-led-matrix" -) - -type RPCMatrix struct { - m rgbmatrix.Matrix -} - -type GeometryArgs struct{} -type GeometryReply struct{ Width, Height int } - -func (m *RPCMatrix) Geometry(_ *GeometryArgs, reply *GeometryReply) error { - w, h := m.m.Geometry() - reply.Width = w - reply.Height = h - - return nil -} - -type ApplyArgs struct{ Colors []color.Color } -type ApplyReply struct{} - -func (m *RPCMatrix) Apply(args *ApplyArgs, reply *ApplyReply) error { - return m.m.Apply(args.Colors) -} - -type CloseArgs struct{} -type CloseReply struct{} - -func (m *RPCMatrix) Close(_ *CloseArgs, _ *CloseReply) error { - return m.m.Close() -} - -func Serve(m rgbmatrix.Matrix) { - rpc.Register(&RPCMatrix{m}) - - rpc.HandleHTTP() - l, e := net.Listen("tcp", ":1234") - if e != nil { - log.Fatal("listen error:", e) - } - - fmt.Println(l) - http.Serve(l, nil) -} diff --git a/go-rpi-rgb-led-matrix/toolkit.go b/go-rpi-rgb-led-matrix/toolkit.go deleted file mode 100644 index f5500d7..0000000 --- a/go-rpi-rgb-led-matrix/toolkit.go +++ /dev/null @@ -1,142 +0,0 @@ -package rgbmatrix - -import ( - "image" - "image/draw" - "image/gif" - "io" - "time" -) - -// ToolKit is a convinient set of function to operate with a led of Matrix -type ToolKit struct { - // Canvas is the Canvas wrapping the Matrix, if you want to instanciate - // a ToolKit with a custom Canvas you can use directly the struct, - // without calling NewToolKit - Canvas *Canvas - - // Transform function if present is applied just before draw the image to - // the Matrix, this is a small example: - // tk.Transform = func(img image.Image) *image.NRGBA { - // return imaging.Fill(img, 64, 96, imaging.Center, imaging.Lanczos) - // } - Transform func(img image.Image) *image.NRGBA -} - -// NewToolKit returns a new ToolKit wrapping the given Matrix -func NewToolKit(m Matrix) *ToolKit { - return &ToolKit{ - Canvas: NewCanvas(m), - } -} - -// PlayImage draws the given image during the given delay -func (tk *ToolKit) PlayImage(i image.Image, delay time.Duration) error { - start := time.Now() - defer func() { time.Sleep(delay - time.Since(start)) }() - - if tk.Transform != nil { - i = tk.Transform(i) - } - - draw.Draw(tk.Canvas, tk.Canvas.Bounds(), i, image.ZP, draw.Over) - return tk.Canvas.Render() -} - -type Animation interface { - Next() (image.Image, <-chan time.Time, error) -} - -// PlayAnimation play the image during the delay returned by Next, until an err -// is returned, if io.EOF is returned, PlayAnimation finish without an error -func (tk *ToolKit) PlayAnimation(a Animation) error { - var err error - var i image.Image - var n <-chan time.Time - - for { - i, n, err = a.Next() - if err != nil { - break - } - - if err := tk.PlayImageUntil(i, n); err != nil { - return err - } - } - - if err == io.EOF { - return nil - } - - return err -} - -// PlayImageUntil draws the given image until is notified to stop -func (tk *ToolKit) PlayImageUntil(i image.Image, notify <-chan time.Time) error { - defer func() { - <-notify - }() - - if tk.Transform != nil { - i = tk.Transform(i) - } - - draw.Draw(tk.Canvas, tk.Canvas.Bounds(), i, image.ZP, draw.Over) - return tk.Canvas.Render() -} - -// PlayImages draws a sequence of images during the given delays, the len of -// images should be equal to the len of delay. If loop is true the function -// loops over images until a true is sent to the returned chan -func (tk *ToolKit) PlayImages(images []image.Image, delay []time.Duration, loop int) chan bool { - quit := make(chan bool, 0) - - go func() { - l := len(images) - i := 0 - for { - select { - case <-quit: - return - default: - tk.PlayImage(images[i], delay[i]) - } - - i++ - if i >= l { - if loop == 0 { - i = 0 - continue - } - - break - } - } - }() - - return quit -} - -// PlayGIF reads and draw a gif file from r. It use the contained images and -// delays and loops over it, until a true is sent to the returned chan -func (tk *ToolKit) PlayGIF(r io.Reader) (chan bool, error) { - gif, err := gif.DecodeAll(r) - if err != nil { - return nil, err - } - - delay := make([]time.Duration, len(gif.Delay)) - images := make([]image.Image, len(gif.Image)) - for i, image := range gif.Image { - images[i] = image - delay[i] = time.Millisecond * time.Duration(gif.Delay[i]) * 10 - } - - return tk.PlayImages(images, delay, gif.LoopCount), nil -} - -// Close close the toolkit and the inner canvas -func (tk *ToolKit) Close() error { - return tk.Canvas.Close() -}