github.com/martinohmann/rfoutlet@v1.2.1-0.20220707195255-8a66aa411105/pkg/gpio/transmitter.go (about) 1 package gpio 2 3 import ( 4 "sync/atomic" 5 "time" 6 7 "github.com/warthog618/gpiod" 8 ) 9 10 const ( 11 // DefaultTransmissionCount defines how many times a code should be 12 // transmitted in a row by default. 13 DefaultTransmissionCount = 10 14 15 transmissionChanLen = 32 16 bitLength = 24 17 ) 18 19 type transmission struct { 20 code uint64 21 protocol Protocol 22 pulseLength uint 23 done chan struct{} 24 } 25 26 // Transmitter can serialize and transmit rf codes. 27 type Transmitter struct { 28 pin OutputPin 29 transmission chan transmission 30 closed int32 31 transmissionCount int 32 // delay can be replaced in tests to make timing predictable. 33 delay func(time.Duration) 34 } 35 36 // NewTransmitter creates a Transmitter which attaches to the chip's pin at 37 // offset. 38 func NewTransmitter(chip *gpiod.Chip, offset int, options ...TransmitterOption) (*Transmitter, error) { 39 pin, err := chip.RequestLine(offset, gpiod.AsOutput(0)) 40 if err != nil { 41 return nil, err 42 } 43 44 return NewPinTransmitter(pin, options...), nil 45 } 46 47 // NewPinTransmitter creates a *Transmitter that sends on pin. 48 func NewPinTransmitter(pin OutputPin, options ...TransmitterOption) *Transmitter { 49 t := &Transmitter{ 50 pin: pin, 51 transmission: make(chan transmission, transmissionChanLen), 52 transmissionCount: DefaultTransmissionCount, 53 delay: delay, 54 } 55 56 for _, option := range options { 57 option(t) 58 } 59 60 if t.transmissionCount <= 0 { 61 t.transmissionCount = 1 62 } 63 64 go t.watch() 65 66 return t 67 } 68 69 // Transmit transmits a code using given protocol and pulse length. 70 // 71 // This method returns immediately. The code is transmitted in the background. 72 // If you need to ensure that a code has been fully transmitted, wait for the 73 // returned channel to be closed. 74 func (t *Transmitter) Transmit(code uint64, protocol Protocol, pulseLength uint) <-chan struct{} { 75 done := make(chan struct{}) 76 77 if atomic.LoadInt32(&t.closed) == 1 { 78 close(done) 79 return done 80 } 81 82 t.transmission <- transmission{ 83 code: code, 84 protocol: protocol, 85 pulseLength: pulseLength, 86 done: done, 87 } 88 89 return done 90 } 91 92 // transmit performs the acutal transmission of the remote control code. 93 func (t *Transmitter) transmit(trans transmission) { 94 defer close(trans.done) 95 96 for i := 0; i < t.transmissionCount; i++ { 97 for j := bitLength - 1; j >= 0; j-- { 98 if trans.code&(1<<uint64(j)) > 0 { 99 t.send(trans.protocol.One, trans.pulseLength) 100 } else { 101 t.send(trans.protocol.Zero, trans.pulseLength) 102 } 103 } 104 t.send(trans.protocol.Sync, trans.pulseLength) 105 } 106 } 107 108 // Close stops started goroutines and closes the gpio pin. 109 func (t *Transmitter) Close() error { 110 atomic.StoreInt32(&t.closed, 0) 111 close(t.transmission) 112 return t.pin.Close() 113 } 114 115 // watch listens on a channel and processes incoming transmissions. 116 func (t *Transmitter) watch() { 117 for trans := range t.transmission { 118 t.transmit(trans) 119 } 120 } 121 122 // send sends a sequence of high and low pulses on the gpio pin. 123 func (t *Transmitter) send(pulses HighLow, pulseLength uint) { 124 t.pin.SetValue(1) 125 t.delay(time.Microsecond * time.Duration(pulseLength*pulses.High)) 126 t.pin.SetValue(0) 127 t.delay(time.Microsecond * time.Duration(pulseLength*pulses.Low)) 128 } 129 130 // NewDiscardingTransmitter creates a *Transmitter that does not send anything. 131 func NewDiscardingTransmitter() *Transmitter { 132 return NewPinTransmitter(&FakeOutputPin{}) 133 }