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

     1  package i2c
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"math"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  // default is for grovepi4 installer
    13  const grovePiDefaultAddress = 0x04
    14  
    15  // commands, see:
    16  // * https://www.dexterindustries.com/GrovePi/programming/grovepi-protocol-adding-custom-sensors/
    17  // * https://github.com/DexterInd/GrovePi/blob/master/Script/multi_grovepi_installer/grovepi4.py
    18  const (
    19  	commandReadDigital         = 1
    20  	commandWriteDigital        = 2
    21  	commandReadAnalog          = 3
    22  	commandWriteAnalog         = 4
    23  	commandSetPinMode          = 5
    24  	commandReadUltrasonic      = 7
    25  	commandReadFirmwareVersion = 8
    26  	commandReadDHT             = 40
    27  )
    28  
    29  // GrovePiDriver is a driver for the GrovePi+ for I²C bus interface.
    30  // https://www.dexterindustries.com/grovepi/
    31  //
    32  // To use this driver with the GrovePi, it must be running the firmware >= 1.4.0 and and the system version >=3.
    33  // https://github.com/DexterInd/GrovePi/blob/master/README.md
    34  //
    35  type GrovePiDriver struct {
    36  	*Driver
    37  	pins map[int]string
    38  }
    39  
    40  // NewGrovePiDriver creates a new driver with specified i2c interface
    41  // Params:
    42  //		conn Connector - the Adaptor to use with this Driver
    43  //
    44  // Optional params:
    45  //		i2c.WithBus(int):	bus to use with this driver
    46  //		i2c.WithAddress(int):	address to use with this driver
    47  //
    48  func NewGrovePiDriver(c Connector, options ...func(Config)) *GrovePiDriver {
    49  	d := &GrovePiDriver{
    50  		Driver: NewDriver(c, "GrovePi", grovePiDefaultAddress),
    51  		pins:   make(map[int]string),
    52  	}
    53  
    54  	for _, option := range options {
    55  		option(d)
    56  	}
    57  
    58  	// TODO: add commands for API
    59  	return d
    60  }
    61  
    62  // Connect is here to implement the Adaptor interface.
    63  func (d *GrovePiDriver) Connect() error {
    64  	return nil
    65  }
    66  
    67  // Finalize is here to implement the Adaptor interface.
    68  func (d *GrovePiDriver) Finalize() error {
    69  	return nil
    70  }
    71  
    72  // AnalogRead returns value from analog pin implementing the AnalogReader interface.
    73  func (d *GrovePiDriver) AnalogRead(pin string) (value int, err error) {
    74  	d.mutex.Lock()
    75  	defer d.mutex.Unlock()
    76  
    77  	pinNum, err := d.preparePin(pin, "input")
    78  	if err != nil {
    79  		return 0, err
    80  	}
    81  
    82  	buf := []byte{commandReadAnalog, byte(pinNum), 0, 0}
    83  	if _, err := d.connection.Write(buf); err != nil {
    84  		return 0, err
    85  	}
    86  
    87  	time.Sleep(2 * time.Millisecond)
    88  
    89  	data := make([]byte, 3)
    90  	if err = d.readForCommand(commandReadAnalog, data); err != nil {
    91  		return 0, err
    92  	}
    93  
    94  	return int(data[1])*256 + int(data[2]), nil
    95  }
    96  
    97  // DigitalRead performs a read on a digital pin.
    98  func (d *GrovePiDriver) DigitalRead(pin string) (int, error) {
    99  	d.mutex.Lock()
   100  	defer d.mutex.Unlock()
   101  
   102  	pinNum, err := d.preparePin(pin, "input")
   103  	if err != nil {
   104  		return 0, err
   105  	}
   106  
   107  	buf := []byte{commandReadDigital, byte(pinNum), 0, 0}
   108  	if _, err := d.connection.Write(buf); err != nil {
   109  		return 0, err
   110  	}
   111  
   112  	time.Sleep(2 * time.Millisecond)
   113  
   114  	data := make([]byte, 2)
   115  	if err = d.readForCommand(commandReadDigital, data); err != nil {
   116  		return 0, err
   117  	}
   118  
   119  	return int(data[1]), nil
   120  }
   121  
   122  // UltrasonicRead performs a read on an ultrasonic pin with duration >=2 millisecond.
   123  func (d *GrovePiDriver) UltrasonicRead(pin string, duration int) (int, error) {
   124  	d.mutex.Lock()
   125  	defer d.mutex.Unlock()
   126  
   127  	if duration < 2 {
   128  		duration = 2
   129  	}
   130  
   131  	pinNum, err := d.preparePin(pin, "input")
   132  	if err != nil {
   133  		return 0, err
   134  	}
   135  
   136  	buf := []byte{commandReadUltrasonic, byte(pinNum), 0, 0}
   137  	if _, err = d.connection.Write(buf); err != nil {
   138  		return 0, err
   139  	}
   140  
   141  	time.Sleep(time.Duration(duration) * time.Millisecond)
   142  
   143  	data := make([]byte, 3)
   144  	if err := d.readForCommand(commandReadUltrasonic, data); err != nil {
   145  		return 0, err
   146  	}
   147  
   148  	return int(data[1])*255 + int(data[2]), nil
   149  }
   150  
   151  // FirmwareVersionRead returns the GrovePi firmware version.
   152  func (d *GrovePiDriver) FirmwareVersionRead() (string, error) {
   153  	d.mutex.Lock()
   154  	defer d.mutex.Unlock()
   155  
   156  	buf := []byte{commandReadFirmwareVersion, 0, 0, 0}
   157  	if _, err := d.connection.Write(buf); err != nil {
   158  		return "", err
   159  	}
   160  
   161  	time.Sleep(2 * time.Millisecond)
   162  
   163  	data := make([]byte, 4)
   164  	if err := d.readForCommand(commandReadFirmwareVersion, data); err != nil {
   165  		return "", err
   166  	}
   167  
   168  	return fmt.Sprintf("%v.%v.%v", data[1], data[2], data[3]), nil
   169  }
   170  
   171  // DHTRead performs a read temperature and humidity sensors with duration >=2 millisecond.
   172  // DHT11 (blue): sensorType=0
   173  // DHT22 (white): sensorTyp=1
   174  func (d *GrovePiDriver) DHTRead(pin string, sensorType byte, duration int) (temp float32, hum float32, err error) {
   175  	d.mutex.Lock()
   176  	defer d.mutex.Unlock()
   177  
   178  	if duration < 2 {
   179  		duration = 2
   180  	}
   181  
   182  	pinNum, err := d.preparePin(pin, "input")
   183  	if err != nil {
   184  		return 0, 0, err
   185  	}
   186  
   187  	buf := []byte{commandReadDHT, byte(pinNum), sensorType, 0}
   188  	if _, err = d.connection.Write(buf); err != nil {
   189  		return
   190  	}
   191  	time.Sleep(time.Duration(duration) * time.Millisecond)
   192  
   193  	data := make([]byte, 9)
   194  	if err = d.readForCommand(commandReadDHT, data); err != nil {
   195  		return
   196  	}
   197  
   198  	temp = float32Of4BytesLittleEndian(data[1:5])
   199  	if temp > 150 {
   200  		temp = 150
   201  	}
   202  	if temp < -100 {
   203  		temp = -100
   204  	}
   205  
   206  	hum = float32Of4BytesLittleEndian(data[5:9])
   207  	if hum > 100 {
   208  		hum = 100
   209  	}
   210  	if hum < 0 {
   211  		hum = 0
   212  	}
   213  
   214  	return
   215  }
   216  
   217  // DigitalWrite writes a value to a specific digital pin implementing the DigitalWriter interface.
   218  func (d *GrovePiDriver) DigitalWrite(pin string, val byte) error {
   219  	d.mutex.Lock()
   220  	defer d.mutex.Unlock()
   221  
   222  	pinNum, err := d.preparePin(pin, "output")
   223  	if err != nil {
   224  		return err
   225  	}
   226  
   227  	buf := []byte{commandWriteDigital, byte(pinNum), val, 0}
   228  	if _, err := d.connection.Write(buf); err != nil {
   229  		return err
   230  	}
   231  
   232  	time.Sleep(2 * time.Millisecond)
   233  
   234  	_, err = d.connection.ReadByte()
   235  	return err
   236  }
   237  
   238  // AnalogWrite writes PWM aka analog to the GrovePi analog pin implementing the AnalogWriter interface.
   239  func (d *GrovePiDriver) AnalogWrite(pin string, val int) error {
   240  	d.mutex.Lock()
   241  	defer d.mutex.Unlock()
   242  
   243  	pinNum, err := d.preparePin(pin, "output")
   244  	if err != nil {
   245  		return err
   246  	}
   247  
   248  	buf := []byte{commandWriteAnalog, byte(pinNum), byte(val), 0}
   249  	if _, err := d.connection.Write(buf); err != nil {
   250  		return err
   251  	}
   252  
   253  	time.Sleep(2 * time.Millisecond)
   254  
   255  	_, err = d.connection.ReadByte()
   256  	return err
   257  }
   258  
   259  // SetPinMode sets the pin mode to input or output.
   260  func (d *GrovePiDriver) SetPinMode(pin byte, mode string) error {
   261  	d.mutex.Lock()
   262  	defer d.mutex.Unlock()
   263  
   264  	return d.setPinMode(pin, mode)
   265  }
   266  
   267  func getPin(pin string) string {
   268  	if len(pin) > 1 {
   269  		if strings.ToUpper(pin[0:1]) == "A" || strings.ToUpper(pin[0:1]) == "D" {
   270  			return pin[1:]
   271  		}
   272  	}
   273  
   274  	return pin
   275  }
   276  
   277  func (d *GrovePiDriver) setPinMode(pin byte, mode string) error {
   278  	var b []byte
   279  	if mode == "output" {
   280  		b = []byte{commandSetPinMode, pin, 1, 0}
   281  	} else {
   282  		b = []byte{commandSetPinMode, pin, 0, 0}
   283  	}
   284  	if _, err := d.connection.Write(b); err != nil {
   285  		return err
   286  	}
   287  
   288  	time.Sleep(2 * time.Millisecond)
   289  
   290  	_, err := d.connection.ReadByte()
   291  	return err
   292  }
   293  
   294  func (d *GrovePiDriver) ensurePinMode(pinNum int, mode string) error {
   295  	if dir, ok := d.pins[pinNum]; !ok || dir != mode {
   296  		if err := d.setPinMode(byte(pinNum), mode); err != nil {
   297  			return err
   298  		}
   299  		d.pins[pinNum] = mode
   300  	}
   301  	return nil
   302  }
   303  
   304  func (d *GrovePiDriver) preparePin(pin string, mode string) (int, error) {
   305  	pin = getPin(pin)
   306  	pinNum, err := strconv.Atoi(pin)
   307  	if err != nil {
   308  		return -1, err
   309  	}
   310  
   311  	if err := d.ensurePinMode(pinNum, mode); err != nil {
   312  		return -1, err
   313  	}
   314  
   315  	return pinNum, nil
   316  }
   317  
   318  func (d *GrovePiDriver) readForCommand(command byte, data []byte) error {
   319  	cnt, err := d.connection.Read(data)
   320  	if err != nil {
   321  		return err
   322  	}
   323  	if len(data) != cnt {
   324  		return fmt.Errorf("read count mismatch (%d should be %d)", cnt, len(data))
   325  	}
   326  	if data[0] != command {
   327  		return fmt.Errorf("answer (%d) was not for command (%d)", data[0], command)
   328  	}
   329  	return nil
   330  }
   331  
   332  func float32Of4BytesLittleEndian(bytes []byte) float32 {
   333  	bits := binary.LittleEndian.Uint32(bytes)
   334  	float := math.Float32frombits(bits)
   335  	return float
   336  }