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  }