updates make install
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
setup:
|
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
|
make
|
||||||
|
|
||||||
run:
|
run:
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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 ./...
|
|
||||||
@@ -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.
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
# go-rpi-rgb-led-matrix [](https://godoc.org/github.com/mcuadros/go-rpi-rgb-led-matrix) [](https://travis-ci.org/mcuadros/go-rpi-rgb-led-matrix)
|
|
||||||
<img width="250" src="https://cloud.githubusercontent.com/assets/1573114/20248154/c17c1f2e-a9dd-11e6-805b-bf7d8ee73121.gif" align="right" />
|
|
||||||
|
|
||||||
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.
|
|
||||||
<img src="https://cloud.githubusercontent.com/assets/1573114/20248173/2e2f97ae-a9de-11e6-95e6-e0548199501d.gif" align="right" width="100" />
|
|
||||||
|
|
||||||
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)
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 <led-matrix-c.h>
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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))
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user