forked from dolmen-go/legodim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
toypad.go
131 lines (114 loc) · 2.18 KB
/
toypad.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package toypad
import (
"encoding/hex"
"io"
"log"
"sync"
)
const (
VendorID = 0x0e6f
ProductID = 0x0241
)
// Pad is one area (or all) of the Toy Pad.
type Pad uint8
const (
AllPads Pad = 0
CenterPad Pad = 1
LeftPad Pad = 2
RightPad Pad = 3
)
// RGB is a color.
type RGB struct {
R, G, B uint8
}
type UID [7]byte
func (uid UID) String() string {
return hex.EncodeToString(uid[:])
}
type tagSlot struct {
pad Pad
uid UID
}
type ToyPad struct {
mu sync.Mutex
w io.Writer
msgId uint8
cb [256]func([]byte, error)
// sendQueue []*Command
tags [12]tagSlot
debug bool
events chan *Event
errors chan error
Events <-chan *Event
Errors <-chan error
}
func NewToyPad(r io.Reader, w io.Writer) (*ToyPad, error) {
events := make(chan *Event, 2)
errors := make(chan error)
toypad := ToyPad{
w: w,
msgId: 0,
events: events,
Events: events,
errors: errors,
Errors: errors,
}
// Init
if err := toypad.Send(Wake()); err != nil {
return nil, err
}
go toypad.readLoop(r)
return &toypad, nil
}
func (tp *ToyPad) Send(cmd *Command) error {
tp.mu.Lock()
defer tp.mu.Unlock()
tp.msgId++
tp.cb[tp.msgId] = cmd.cb
return cmd.Send(tp.w, tp.msgId)
}
func (tp *ToyPad) readLoop(r io.Reader) {
for {
var frame [32]byte
_, err := r.Read(frame[:])
if err != nil {
log.Println(err)
break
}
// log.Printf("[% X]", frame[:])
payload, err := parseFrame(frame)
if err != nil {
tp.errors <- err
log.Printf("[% X] %v", frame[:], err)
continue
}
l := int(frame[1])
log.Printf("<< [% X] %[1]q", frame[:2+l])
switch frame[0] {
case 'V': // Event
ev, err := parseEvent(payload)
if err != nil {
tp.errors <- err
} else {
tp.mu.Lock()
if ev.Action == Add {
tp.tags[int(ev.Index)] = tagSlot{pad: ev.Pad, uid: ev.UID}
} else {
tp.tags[int(ev.Index)] = tagSlot{} // Clear the slot
}
tp.mu.Unlock()
tp.events <- ev
}
case 'U': // Reply to a request
log.Printf("Reply to msg %d [% X]", payload[0], payload[1:])
msgId := int(payload[0])
cb := tp.cb[msgId]
tp.cb[msgId] = nil
if cb != nil {
cb(payload[1:], nil)
}
default:
log.Printf("Unknown frame type %02X", frame[0])
}
}
}