Skip to content

Instantly share code, notes, and snippets.

@artvel
Created April 1, 2021 18:21
Show Gist options
  • Save artvel/cb46e66f2e295af191e4df89668b9460 to your computer and use it in GitHub Desktop.
Save artvel/cb46e66f2e295af191e4df89668b9460 to your computer and use it in GitHub Desktop.
QNAP TVS-472XT-PT-4G LED Display Controller
package main
import (
"display"
"fmt"
"log"
"os"
"os/signal"
)
//Example of how to use
func main() {
led, err := display.NewQnapLED()
if err != nil {
log.Fatal(err)
}
fmt.Println(led.Write(0, "The Name!"))
fmt.Println(led.Write(1, "Muuuuu"))
go led.Listen(func(btn int, released bool) bool {
pressed := ""
if released {
pressed = "-"
if btn == 1 {
return false
}
} else {
pressed = "+"
}
_ = led.Write(0, fmt.Sprintf("Button test! %s %d", pressed, btn))
log.Println("btn", btn, "released", released)
return true
})
defer func() {
_ = led.Enable(false)
_ = led.Close()
}()
interruptWaiter := make(chan os.Signal)
signal.Notify(interruptWaiter, os.Interrupt)
<-interruptWaiter
}
package display
import (
"bytes"
"encoding/hex"
"fmt"
"github.com/jacobsa/go-serial/serial"
"github.com/pkg/errors"
"io"
)
type LED interface {
Listen(l func(btn int, released bool) bool)
Enable(yes bool) error
Write(line int, text string) error
Open() error
Close() error
}
type qnap struct {
released []byte
upPressed []byte
downPressed []byte
bothPressed []byte
cmdEnable []byte
cmdDisable []byte
cmdWrite []byte
cmdInit []byte
cmdRdy []byte
con io.ReadWriteCloser
open bool
}
var ErrDisplayNotWorking = errors.New("display not working")
func NewQnapLED() (LED, error) {
q := &qnap{
released: []byte{83, 5, 0, 0},
upPressed: []byte{83, 5, 0, 1},
downPressed: []byte{83, 5, 0, 2},
bothPressed: []byte{83, 5, 0, 3},
cmdEnable: []byte{77, 94, 1, 10},
cmdDisable: []byte{77, 94, 0, 10},
cmdWrite: []byte{77, 94, 1},
cmdInit: []byte{77, 0},
cmdRdy: []byte{83, 1, 0, 125},
}
err := q.init()
return q, err
}
func (q *qnap) Open() error {
if q.open {
return nil
}
return q.init()
}
func (q *qnap) Enable(yes bool) error {
if !q.open {
return ErrDisplayNotWorking
}
if yes {
_, err := q.con.Write(q.cmdEnable)
return err
} else {
_, err := q.con.Write(q.cmdDisable)
return err
}
}
func (q *qnap) Write(line int, txt string) error {
if !q.open {
return ErrDisplayNotWorking
}
if len(txt) > 16 {
//cut if longer
txt = txt[:16]
} else {
//fill rest empty
for len(txt) < 16 {
txt += " "
}
}
cnt := []byte(fmt.Sprintf("%s%s", h(fmt.Sprintf("4d0c0%d10", line)), txt))
_, err := q.con.Write(q.cmdWrite)
if err != nil {
return err
}
_, err = q.con.Write(cnt)
return err
}
func (q *qnap) Listen(l func(btn int, released bool) bool) {
if !q.open {
return
}
defer func() {
if r := recover(); r != nil {
}
}()
var lastBtn = 0
for {
res := make([]byte, 4)
n, err := q.con.Read(res)
if err != nil {
return
}
if n != len(res) {
continue
}
if bytes.Equal(res, q.released) {
if !l(lastBtn, true) {
return
}
lastBtn = 0
} else if bytes.Equal(res, q.upPressed) {
if lastBtn == 3 {
continue
}
lastBtn = 1
if !l(lastBtn, false) {
return
}
} else if bytes.Equal(res, q.downPressed) {
if lastBtn == 3 {
continue
}
lastBtn = 2
if !l(lastBtn, false) {
return
}
} else if bytes.Equal(res, q.bothPressed) {
lastBtn = 3
if !l(lastBtn, false) {
return
}
}
}
}
func (q *qnap) Close() error {
if !q.open {
return nil
}
if q.con == nil {
return nil
}
return q.con.Close()
}
func (q *qnap) init() error {
var err error
q.con, err = serial.Open(serial.OpenOptions{
PortName: "/dev/ttyS1",
BaudRate: 1200,
DataBits: 8,
StopBits: 1,
MinimumReadSize: 4,
})
if err != nil {
return err
}
defer func() {
if !q.open {
_ = q.con.Close()
}
}()
_, err = q.con.Write(q.cmdInit)
if err != nil {
return err
}
res := make([]byte, 4)
_, err = q.con.Read(res)
if err != nil {
return err
}
if bytes.Equal(res, q.cmdRdy) {
q.open = true
return nil
} else {
q.open = false
return ErrDisplayNotWorking
}
}
func h(s string) []byte {
decoded, _ := hex.DecodeString(s)
return decoded
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment