-
Notifications
You must be signed in to change notification settings - Fork 7
/
gpio.go
195 lines (146 loc) · 4.22 KB
/
gpio.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// GPIO pin output from tally state
//
// Supports two status pins and N tally pins.
//
// The green status pin is high to indicate tally activity. In the idle state (no tally sources discovered), the green
// status pin blinks on slowly. In the active state (at least one tally source connected), the green status pin is on,
// and blinks off on changes.
//
// The red status pin is normally low, and is set high when there are any tally source errors.
//
// Each tally pin corresponds to the sequentially numbered tally ID. It will be set high when the tally ID is out on
// any output program.
package gpio
import (
"fmt"
"log"
"sync"
"time"
"github.com/kidoman/embd"
_ "github.com/kidoman/embd/host/rpi" // This loads the RPi driver
"github.com/qmsk/e2/tally"
)
type Options struct {
StatusGreenPin string `long:"gpio-green-pin" value-name:"GPIO-PIN" description:"GPIO pin for green status LED"`
StatusRedPin string `long:"gpio-red-pin" value-name:"GPIO-PIN" description:"GPIO pin for red status LED"`
TallyPins []string `long:"gpio-tally-pin" value-name:"GPIO-PIN" description:"Pass each tally pin as a separate option"`
}
func (options Options) Make() (*GPIO, error) {
var gpio = GPIO{
options: options,
tallyPins: make(map[tally.ID]*Pin),
}
if err := gpio.init(options); err != nil {
return nil, err
}
return &gpio, nil
}
type GPIO struct {
options Options
tallyPins map[tally.ID]*Pin
// red pin is high if there are sources with errors
statusRedPin *Pin
// green pin is high if there are sources with tallys
statusGreenPin *Pin
tallyChan chan tally.State
waitGroup sync.WaitGroup
}
func (gpio *GPIO) init(options Options) error {
if err := embd.InitGPIO(); err != nil {
return fmt.Errorf("embd.InitGPIO: %v", err)
}
for i, pinName := range options.TallyPins {
id := tally.ID(i + 1)
if pin, err := openPin(fmt.Sprintf("tally:%d", id), pinName); err != nil {
return err
} else {
gpio.tallyPins[tally.ID(i+1)] = pin
}
}
if options.StatusGreenPin == "" {
} else if pin, err := openPin("status:green", options.StatusGreenPin); err != nil {
return err
} else {
gpio.statusGreenPin = pin
}
if options.StatusRedPin == "" {
} else if pin, err := openPin("status:red", options.StatusRedPin); err != nil {
return err
} else {
gpio.statusRedPin = pin
}
return nil
}
func (gpio *GPIO) RegisterTally(t *tally.Tally) {
gpio.tallyChan = make(chan tally.State)
gpio.waitGroup.Add(1)
go gpio.run()
t.Register(gpio.tallyChan)
}
func (gpio *GPIO) close() {
defer gpio.waitGroup.Done()
log.Printf("GPIO: Close pins..")
if gpio.statusGreenPin != nil {
gpio.statusGreenPin.Close(&gpio.waitGroup)
}
if gpio.statusRedPin != nil {
gpio.statusRedPin.Close(&gpio.waitGroup)
}
for _, pin := range gpio.tallyPins {
pin.Close(&gpio.waitGroup)
}
}
func (gpio *GPIO) updateTally(state tally.State) {
log.Printf("GPIO: Update tally State:")
var statusGreen = false
var statusRed = false
for id, pin := range gpio.tallyPins {
var pinState = false
if status, exists := state.Tally[id]; !exists {
// missing tally state for pin
} else {
statusGreen = true
if status.Status.High() {
log.Printf("GPIO:\ttally pin %v high: %v", pin, status)
pinState = true
}
}
pin.Set(pinState)
}
if len(state.Errors) > 0 {
statusRed = true
}
// update status leds
if gpio.statusGreenPin == nil {
} else if statusGreen {
log.Printf("GPIO: status:green high: blink")
// when connected, blink off for 100ms on every update
gpio.statusGreenPin.Blink(false, 100*time.Millisecond)
} else {
log.Printf("GPIO: status:green low: cycle")
// when not connected, blink on for 100ms every 1s
gpio.statusGreenPin.BlinkCycle(true, 100*time.Millisecond, 1*time.Second)
}
if gpio.statusRedPin == nil {
} else if statusRed {
log.Printf("GPIO: status:red blink: cycle")
gpio.statusRedPin.BlinkCycle(true, 500*time.Millisecond, 500*time.Millisecond)
} else {
gpio.statusRedPin.Set(false)
}
}
func (gpio *GPIO) run() {
defer gpio.close()
for state := range gpio.tallyChan {
gpio.updateTally(state)
}
log.Printf("GPIO: Done")
}
// Close and Wait..
func (gpio *GPIO) Close() {
log.Printf("GPIO: Close..")
if gpio.tallyChan != nil {
close(gpio.tallyChan)
}
gpio.waitGroup.Wait()
}