gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/i2c_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"strconv"
     7  	"sync"
     8  
     9  	"gobot.io/x/gobot/v2"
    10  )
    11  
    12  // Config is the interface to set and get I2C device related parameters.
    13  type Config interface {
    14  	// SetBus sets which bus to use
    15  	SetBus(bus int)
    16  
    17  	// GetBusOrDefault gets which bus to use
    18  	GetBusOrDefault(def int) int
    19  
    20  	// SetAddress sets which address to use
    21  	SetAddress(address int)
    22  
    23  	// GetAddressOrDefault gets which address to use
    24  	GetAddressOrDefault(def int) int
    25  }
    26  
    27  // Connector lets adaptors (platforms) provide the interface for Drivers to get access to the I2C buses on platforms
    28  // that support I2C. The "I2C" specifier is part of the name to differentiate to SPI at platform level.
    29  type Connector interface {
    30  	// GetI2cConnection creates and returns a connection to device at the specified address
    31  	// and bus. Bus numbering starts at index 0, the range of valid buses is
    32  	// platform specific.
    33  	GetI2cConnection(address int, busNr int) (device Connection, err error)
    34  
    35  	// DefaultI2cBus returns the default I2C bus index
    36  	DefaultI2cBus() int
    37  }
    38  
    39  // Driver implements the interface gobot.Driver.
    40  type Driver struct {
    41  	name           string
    42  	defaultAddress int
    43  	connector      Connector
    44  	connection     Connection
    45  	afterStart     func() error
    46  	beforeHalt     func() error
    47  	Config
    48  	gobot.Commander
    49  	mutex *sync.Mutex // mutex often needed to ensure that write-read sequences are not interrupted
    50  }
    51  
    52  // NewDriver creates a new generic and basic i2c gobot driver.
    53  func NewDriver(c Connector, name string, address int, options ...func(Config)) *Driver {
    54  	d := &Driver{
    55  		name:           gobot.DefaultName(name),
    56  		defaultAddress: address,
    57  		connector:      c,
    58  		afterStart:     func() error { return nil },
    59  		beforeHalt:     func() error { return nil },
    60  		Config:         NewConfig(),
    61  		Commander:      gobot.NewCommander(),
    62  		mutex:          &sync.Mutex{},
    63  	}
    64  
    65  	for _, option := range options {
    66  		option(d)
    67  	}
    68  
    69  	return d
    70  }
    71  
    72  // Name returns the name of the i2c device.
    73  func (d *Driver) Name() string {
    74  	return d.name
    75  }
    76  
    77  // SetName sets the name of the i2c device.
    78  func (d *Driver) SetName(name string) {
    79  	d.name = name
    80  }
    81  
    82  // Connection returns the connection of the i2c device.
    83  func (d *Driver) Connection() gobot.Connection {
    84  	return d.connector.(gobot.Connection)
    85  }
    86  
    87  // Start initializes the i2c device.
    88  func (d *Driver) Start() error {
    89  	d.mutex.Lock()
    90  	defer d.mutex.Unlock()
    91  
    92  	var err error
    93  	bus := d.GetBusOrDefault(d.connector.DefaultI2cBus())
    94  	address := d.GetAddressOrDefault(int(d.defaultAddress))
    95  
    96  	if d.connection, err = d.connector.GetI2cConnection(address, bus); err != nil {
    97  		return err
    98  	}
    99  
   100  	return d.afterStart()
   101  }
   102  
   103  // Halt halts the i2c device.
   104  func (d *Driver) Halt() error {
   105  	d.mutex.Lock()
   106  	defer d.mutex.Unlock()
   107  
   108  	if err := d.beforeHalt(); err != nil {
   109  		return err
   110  	}
   111  
   112  	// currently there is nothing to do here for the driver
   113  	return nil
   114  }
   115  
   116  // Write implements a simple write mechanism, starting from the given register of an i2c device.
   117  func (d *Driver) Write(pin string, val int) error {
   118  	d.mutex.Lock()
   119  	defer d.mutex.Unlock()
   120  
   121  	register, err := driverParseRegister(pin)
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	if val > 0xFFFF {
   127  		buf := make([]byte, 4)
   128  		binary.LittleEndian.PutUint32(buf, uint32(val))
   129  		return d.connection.WriteBlockData(register, buf)
   130  	}
   131  	if val > 0xFF {
   132  		return d.connection.WriteWordData(register, uint16(val))
   133  	}
   134  	return d.connection.WriteByteData(register, uint8(val))
   135  }
   136  
   137  // Read implements a simple read mechanism from the given register of an i2c device.
   138  func (d *Driver) Read(pin string) (int, error) {
   139  	d.mutex.Lock()
   140  	defer d.mutex.Unlock()
   141  
   142  	register, err := driverParseRegister(pin)
   143  	if err != nil {
   144  		return 0, err
   145  	}
   146  
   147  	val, err := d.connection.ReadByteData(register)
   148  	if err != nil {
   149  		return 0, err
   150  	}
   151  
   152  	return int(val), nil
   153  }
   154  
   155  func driverParseRegister(pin string) (uint8, error) {
   156  	register, err := strconv.ParseUint(pin, 10, 8)
   157  	if err != nil {
   158  		return 0, fmt.Errorf("Could not parse the register from given pin '%s'", pin)
   159  	}
   160  	return uint8(register), nil
   161  }