updates make install
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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