Skip to content

Commit

Permalink
Merge pull request #18 from richardlt/features/autoConnectGamepadAndM…
Browse files Browse the repository at this point in the history
…atrix

Features/auto connect gamepad and matrix
  • Loading branch information
richardlt authored Nov 18, 2018
2 parents 4a3b207 + 9cdf6a5 commit 49626c5
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 83 deletions.
78 changes: 58 additions & 20 deletions device/gamepad.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"fmt"
"sort"
"sync"
"time"

"github.com/karalabe/hid"
"github.com/pkg/errors"
Expand Down Expand Up @@ -40,8 +42,7 @@ type device struct {
HID hid.DeviceInfo
Conf config
States map[string]bool
Index int
Key string
Slot int
}

type action struct {
Expand All @@ -54,20 +55,57 @@ func (g *gamepad) OpenDevices(ctx context.Context) error {
defer close(cAction)

vid, pid := uint16(0x0079), uint16(0x0011)
devs := hid.Enumerate(vid, pid)

logrus.Debugf("Found %d controller", len(devs))

for i := 0; i < len(devs); i++ {
key := fmt.Sprintf("%d_%d", devs[i].VendorID, devs[i].ProductID)
go g.listenDevice(cAction, &device{
HID: devs[i],
Conf: g.configs[key],
States: map[string]bool{},
Index: i,
Key: key,
})
}

go func() {
connected := map[string]*device{}
mutex := new(sync.Mutex)

for {
if ctx.Err() != nil {
return
}

mutex.Lock()

mFreeSlots := map[int]bool{0: true, 1: true, 2: true, 3: true}
for _, d := range connected {
mFreeSlots[d.Slot] = false
}
freeSlots := []int{}
for i := 0; i < len(mFreeSlots); i++ {
if mFreeSlots[i] {
freeSlots = append(freeSlots, i)
}
}

if len(freeSlots) > 0 {
devs := hid.Enumerate(vid, pid)
for i := 0; i < len(devs) && i < len(freeSlots); i++ {
if _, ok := connected[devs[i].Path]; !ok {
key := fmt.Sprintf("%d_%d", devs[i].VendorID, devs[i].ProductID)
d := &device{
HID: devs[i],
Conf: g.configs[key],
States: map[string]bool{},
Slot: freeSlots[i],
}
connected[d.HID.Path] = d
logrus.Infof("Controller %s connected on slot %d", d.HID.Path, d.Slot)
go func(d *device) {
g.listenDevice(cAction, d)
mutex.Lock()
delete(connected, d.HID.Path)
mutex.Unlock()
logrus.Infof("Controller %s at slot %d disconnected", d.HID.Path, d.Slot)
}(d)
}
}
}

mutex.Unlock()
time.Sleep(time.Second)
}
}()

for {
select {
Expand All @@ -82,7 +120,7 @@ func (g *gamepad) OpenDevices(ctx context.Context) error {
}

func (g *gamepad) listenDevice(cAction chan action, dev *device) {
defer logrus.Debugf("Stop listening from %s controller", dev.Key)
defer logrus.Debugf("Stop listening from %s controller", dev.HID.Path)

d, err := dev.HID.Open()
if err != nil {
Expand All @@ -95,7 +133,7 @@ func (g *gamepad) listenDevice(cAction chan action, dev *device) {
}
}()

logrus.Debugf("Listening from %s controller", dev.Key)
logrus.Debugf("Listening from %s controller", dev.HID.Path)

handler := g.handleData(dev)

Expand Down Expand Up @@ -141,9 +179,9 @@ func (g *gamepad) handleData(dev *device) func([]byte) *action {

var a *action
if isPressed && !currentState {
a = &action{Slot: dev.Index, Command: commandFromString(b.Name + ":press")}
a = &action{Slot: dev.Slot, Command: commandFromString(b.Name + ":press")}
} else if !isPressed && currentState {
a = &action{Slot: dev.Index, Command: commandFromString(b.Name + ":release")}
a = &action{Slot: dev.Slot, Command: commandFromString(b.Name + ":release")}
}

dev.States[b.Name] = isPressed
Expand Down
161 changes: 98 additions & 63 deletions device/matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"path/filepath"
"strings"
"sync"
"time"

"github.com/pkg/errors"
Expand Down Expand Up @@ -136,81 +137,115 @@ func (m *matrix) ActionReceived(slot uint64, cmd common.Command) {
}

func (m *matrix) OpenPorts(ctx context.Context) error {
matches, err := filepath.Glob("/dev/tty*")
if err != nil {
return errors.WithStack(err)
}

paths := []string{}
for _, ma := range matches {
if strings.Contains(strings.ToLower(ma), "usb") {
paths = append(paths, ma)
}
}

for _, path := range paths {
logrus.Debugf("Try to open port at %s", path)

port, err := serial.Open(path, &serial.Mode{BaudRate: 115200})
if err != nil {
return errors.WithStack(err)
}

logrus.Debugf("Port opened at %s", path)
connected := map[string]struct{}{}
mutex := new(sync.Mutex)

if err := port.ResetInputBuffer(); err != nil {
return errors.WithStack(err)
}
if err := port.ResetOutputBuffer(); err != nil {
return errors.WithStack(err)
}
go func() {
for {
if ctx.Err() != nil {
return
}

// read the matrix size
buf := make([]byte, 1)
_, err = port.Read(buf)
if err != nil {
return errors.WithStack(err)
}
mutex.Lock()
defered := func() {
mutex.Unlock()
time.Sleep(time.Second)
}

size := int(buf[0])
matches, err := filepath.Glob("/dev/tty*")
if err != nil {
logrus.Errorf("%+v", errors.WithStack(err))
defered()
continue
}

logrus.Debugf("Receive %d size for port at %s", size, path)
paths := []string{}
for _, ma := range matches {
if strings.Contains(strings.ToLower(ma), "usb") {
paths = append(paths, ma)
}
}

go func(path string) {
t := time.NewTicker(refreshDelay)
defer t.Stop()
for _, path := range paths {
if _, ok := connected[path]; !ok {
logrus.Debugf("Try to open port at %s", path)

var lastBuffer []byte
for {
select {
case <-ctx.Done():
return
case <-t.C:
if !bytes.Equal(lastBuffer, m.buffer) {
lastBuffer = m.buffer
port, err := serial.Open(path, &serial.Mode{BaudRate: 115200})
if err != nil {
logrus.Errorf("%+v", errors.WithStack(err))
continue
}

buffer := make([]byte, size*3+1)
for i := 0; i < len(m.buffer) && i < len(buffer); i++ {
buffer[i] = m.buffer[i]
}
logrus.Debugf("Port opened at %s", path)

if _, err := port.Write(buffer); err != nil {
logrus.Errorf("%+v", errors.WithStack(err))
return
}
if err := port.ResetInputBuffer(); err != nil {
logrus.Errorf("%+v", errors.WithStack(err))
continue
}
if err := port.ResetOutputBuffer(); err != nil {
logrus.Errorf("%+v", errors.WithStack(err))
continue
}

// read the ack
ack := make([]byte, 1)
_, err = port.Read(ack)
if err != nil {
logrus.Errorf("%+v", errors.WithStack(err))
return
}
// read the matrix size
buf := make([]byte, 1)
if _, err := port.Read(buf); err != nil {
logrus.Errorf("%+v", errors.WithStack(err))
continue
}
size := int(buf[0])
logrus.Debugf("Receive %d size for port at %s", size, path)

connected[path] = struct{}{}
logrus.Infof("Serial %s connected", path)

go func(path string) {
t := time.NewTicker(refreshDelay)
defer func() {
t.Stop()

mutex.Lock()
delete(connected, path)
mutex.Unlock()
logrus.Infof("Serial %s disconnected", path)
}()

var lastBuffer []byte
for {
select {
case <-ctx.Done():
return
case <-t.C:
if !bytes.Equal(lastBuffer, m.buffer) {
lastBuffer = m.buffer

buffer := make([]byte, size*3+1)
for i := 0; i < len(m.buffer) && i < len(buffer); i++ {
buffer[i] = m.buffer[i]
}

if _, err := port.Write(buffer); err != nil {
logrus.Errorf("%+v", errors.WithStack(err))
return
}

// read the ack
ack := make([]byte, 1)
_, err = port.Read(ack)
if err != nil {
logrus.Errorf("%+v", errors.WithStack(err))
return
}
}
}
}
}(path)
}
}
}(path)
}

defered()
}
}()

return nil
}
Expand Down

0 comments on commit 49626c5

Please sign in to comment.