LedD.PCA9685/main.go

193 lines
4.1 KiB
Go

package main
import (
"encoding/binary"
"fmt"
"gen/ledd"
"github.com/golang/protobuf/proto"
"github.com/op/go-logging"
"golang.org/x/exp/io/i2c"
"gopkg.in/yaml.v2"
"io/ioutil"
"math"
"net"
"os"
"os/signal"
"syscall"
)
// CONSTANTS
const VERSION = "0.1"
const RESOLUTION = 4095
const CHANNEL = 16
// STRUCTS
type config struct {
Name string
Ledd struct {
Host string
Port int
}
Pca9685 struct {
Device string
Address int
MinPulse uint16
MaxPulse uint16
Gamma float64
}
}
type LedDaemon struct {
name string
socket net.Conn
data chan []byte
}
var log = logging.MustGetLogger("LedD")
var ledDaemon *LedDaemon
var pca9685 *PCA9685
var pwmMap map[int32]*Pwm
var readConfig config
func check(e error) {
if e != nil {
panic(e)
}
}
func (daemon *LedDaemon) receive() {
for {
message := make([]byte, 8192)
length, err := daemon.socket.Read(message)
if err != nil {
daemon.socket.Close()
break
}
if length > 0 {
log.Debugf("[%s] Read %d bytes", daemon.socket.RemoteAddr(), length)
for i := 0; i < length; {
msgLen := int(binary.BigEndian.Uint32(message[i : i+4]))
log.Debugf("[%s] Reading protobuf after %d (len=%d)", daemon.socket.RemoteAddr(), i, msgLen)
if i+msgLen+4 > len(message)-1 {
log.Warningf("[%s] Buffer overflow. At least one message has been discarded!", daemon.socket.RemoteAddr())
break
}
backendMsg := &ledd.BackendWrapperMessage{}
err = proto.Unmarshal(message[i+4:i+msgLen+4], backendMsg)
i += msgLen + 4
if err != nil {
log.Warningf("[%s] Couldn't decode protobuf msg!", daemon.name)
continue
}
switch msg := backendMsg.Msg.(type) {
case *ledd.BackendWrapperMessage_MLedd:
daemon.name = msg.MLedd.Name
log.Infof("Connection with %s established; backend registered", msg.MLedd.Name)
case *ledd.BackendWrapperMessage_MSetChannel:
for c, v := range msg.MSetChannel.Values {
if c > CHANNEL {
log.Warningf("[%s] Channel index %d is higher then this device's max channel index %d. Skipping.", daemon.name, c, CHANNEL)
continue
}
v = math.Pow(v, 1/readConfig.Pca9685.Gamma)
if pwm, ok := pwmMap[c]; ok {
pwm.setPercentage(float32(v) * 100 * msg.MSetChannel.Correction[c])
} else {
pwmMap[c] = pca9685.NewPwm(int(c))
pwm.setPercentage(float32(v) * 100 * msg.MSetChannel.Correction[c])
}
}
}
}
}
}
}
func (daemon *LedDaemon) send() {
defer daemon.socket.Close()
for {
select {
case message, ok := <-daemon.data:
if !ok {
return
}
daemon.socket.Write(message)
}
}
}
func prepareProtobuf(data []byte) []byte {
size := make([]byte, 4)
binary.BigEndian.PutUint32(size, uint32(len(data)))
return append(size, data...)
}
func main() {
killSignals := make(chan os.Signal, 1)
signal.Notify(killSignals, syscall.SIGINT, syscall.SIGTERM)
log.Info("LedD PCA9685 backend", VERSION)
content, err := ioutil.ReadFile("config.yaml")
check(err)
err = yaml.Unmarshal(content, &readConfig)
check(err)
i2cDevice, err := i2c.Open(&i2c.Devfs{Dev: readConfig.Pca9685.Device}, readConfig.Pca9685.Address)
check(err)
defer i2cDevice.Close()
pca9685 = createPCA9685(i2cDevice, readConfig.Name, readConfig.Pca9685.MinPulse, readConfig.Pca9685.MaxPulse, logging.MustGetLogger("PCA9685"))
pca9685.Init()
pwmMap = make(map[int32]*Pwm, 0)
conn, err := net.Dial("tcp4", fmt.Sprintf("%s:%d", readConfig.Ledd.Host, readConfig.Ledd.Port))
check(err)
ledDaemon = &LedDaemon{
socket: conn,
data: make(chan []byte, 20),
name: "?",
}
go ledDaemon.send()
go ledDaemon.receive()
wrapperMsg := &ledd.BackendWrapperMessage{
Msg: &ledd.BackendWrapperMessage_MBackend{
MBackend: &ledd.Backend{
Name: readConfig.Name,
Channel: CHANNEL,
Type: "PCA9685",
Resolution: RESOLUTION,
Version: VERSION,
},
},
}
data, err := proto.Marshal(wrapperMsg)
check(err)
ledDaemon.data <- prepareProtobuf(data)
<-killSignals
keys := make([]int, 0, len(pwmMap))
for c := range pwmMap {
keys = append(keys, int(c))
}
pca9685.SwitchOff(keys)
}