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

     1  package i2c
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"log"
     7  	"time"
     8  )
     9  
    10  const bmp180Debug = false
    11  
    12  // the default address is applicable for SDO to VDD, for SDO to GND it will be 0x76
    13  const bmp180DefaultAddress = 0x77
    14  
    15  const (
    16  	bmp180RegisterAC1MSB  = 0xAA // 11 x 16 bit calibration data (AC1..AC6, B1, B2, MB, MC, MD)
    17  	bmp180RegisterCtl     = 0xF4 // control the value to read
    18  	bmp180RegisterDataMSB = 0xF6 // 16 bit data (temperature or pressure)
    19  
    20  	bmp180CtlTemp     = 0x2E
    21  	bmp180CtlPressure = 0x34
    22  )
    23  
    24  const (
    25  	// BMP180UltraLowPower is the lowest oversampling mode of the pressure measurement.
    26  	BMP180UltraLowPower BMP180OversamplingMode = iota
    27  	// BMP180Standard is the standard oversampling mode of the pressure measurement.
    28  	BMP180Standard
    29  	// BMP180HighResolution is a high oversampling mode of the pressure measurement.
    30  	BMP180HighResolution
    31  	// BMP180UltraHighResolution is the highest oversampling mode of the pressure measurement.
    32  	BMP180UltraHighResolution
    33  )
    34  
    35  // BMP180OversamplingMode is the oversampling ratio of the pressure measurement.
    36  type BMP180OversamplingMode uint
    37  
    38  type bmp180CalibrationCoefficients struct {
    39  	ac1 int16
    40  	ac2 int16
    41  	ac3 int16
    42  	ac4 uint16
    43  	ac5 uint16
    44  	ac6 uint16
    45  	b1  int16
    46  	b2  int16
    47  	mb  int16
    48  	mc  int16
    49  	md  int16
    50  }
    51  
    52  // BMP180Driver is the gobot driver for the Bosch pressure and temperature sensor BMP180.
    53  // Device datasheet: https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
    54  type BMP180Driver struct {
    55  	*Driver
    56  	oversampling BMP180OversamplingMode
    57  	calCoeffs    *bmp180CalibrationCoefficients
    58  }
    59  
    60  // NewBMP180Driver creates a new driver with the i2c interface for the BMP180 device.
    61  // Params:
    62  //		conn Connector - the Adaptor to use with this Driver
    63  //
    64  // Optional params:
    65  //		i2c.WithBus(int):	bus to use with this driver
    66  //		i2c.WithAddress(int):	address to use with this driver
    67  //
    68  func NewBMP180Driver(c Connector, options ...func(Config)) *BMP180Driver {
    69  	d := &BMP180Driver{
    70  		Driver:       NewDriver(c, "BMP180", bmp180DefaultAddress),
    71  		oversampling: BMP180UltraLowPower,
    72  		calCoeffs:    &bmp180CalibrationCoefficients{},
    73  	}
    74  	d.afterStart = d.initialization
    75  
    76  	for _, option := range options {
    77  		option(d)
    78  	}
    79  
    80  	// TODO: expose commands to API
    81  	return d
    82  }
    83  
    84  // WithBMP180oversampling option sets oversampling mode.
    85  // Valid settings are of type "BMP180OversamplingMode"
    86  func WithBMP180OversamplingMode(val BMP180OversamplingMode) func(Config) {
    87  	return func(c Config) {
    88  		if d, ok := c.(*BMP180Driver); ok {
    89  			d.oversampling = val
    90  		} else if bmp180Debug {
    91  			log.Printf("Trying to set oversampling mode for non-BMP180Driver %v", c)
    92  		}
    93  	}
    94  }
    95  
    96  // Temperature returns the current temperature, in celsius degrees.
    97  func (d *BMP180Driver) Temperature() (temp float32, err error) {
    98  	d.mutex.Lock()
    99  	defer d.mutex.Unlock()
   100  
   101  	var rawTemp int16
   102  	if rawTemp, err = d.rawTemp(); err != nil {
   103  		return 0, err
   104  	}
   105  	return d.calculateTemp(rawTemp), nil
   106  }
   107  
   108  // Pressure returns the current pressure, in pascals.
   109  func (d *BMP180Driver) Pressure() (pressure float32, err error) {
   110  	d.mutex.Lock()
   111  	defer d.mutex.Unlock()
   112  
   113  	var rawTemp int16
   114  	var rawPressure int32
   115  	if rawTemp, err = d.rawTemp(); err != nil {
   116  		return 0, err
   117  	}
   118  	if rawPressure, err = d.rawPressure(d.oversampling); err != nil {
   119  		return 0, err
   120  	}
   121  	return d.calculatePressure(rawTemp, rawPressure, d.oversampling), nil
   122  }
   123  
   124  func (d *BMP180Driver) initialization() (err error) {
   125  	// read the 11 calibration coefficients.
   126  	coefficients := make([]byte, 22)
   127  	if err = d.connection.ReadBlockData(bmp180RegisterAC1MSB, coefficients); err != nil {
   128  		return err
   129  	}
   130  	buf := bytes.NewBuffer(coefficients)
   131  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac1)
   132  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac2)
   133  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac3)
   134  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac4)
   135  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac5)
   136  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac6)
   137  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.b1)
   138  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.b2)
   139  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.mb)
   140  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.mc)
   141  	binary.Read(buf, binary.BigEndian, &d.calCoeffs.md)
   142  
   143  	return nil
   144  }
   145  
   146  func (d *BMP180Driver) rawTemp() (int16, error) {
   147  	if _, err := d.connection.Write([]byte{bmp180RegisterCtl, bmp180CtlTemp}); err != nil {
   148  		return 0, err
   149  	}
   150  	time.Sleep(5 * time.Millisecond)
   151  	ret := make([]byte, 2)
   152  	err := d.connection.ReadBlockData(bmp180RegisterDataMSB, ret)
   153  	if err != nil {
   154  		return 0, err
   155  	}
   156  	buf := bytes.NewBuffer(ret)
   157  	var rawTemp int16
   158  	binary.Read(buf, binary.BigEndian, &rawTemp)
   159  	return rawTemp, nil
   160  }
   161  
   162  func (d *BMP180Driver) calculateTemp(rawTemp int16) float32 {
   163  	b5 := d.calculateB5(rawTemp)
   164  	t := (b5 + 8) >> 4
   165  	return float32(t) / 10
   166  }
   167  
   168  func (d *BMP180Driver) calculateB5(rawTemp int16) int32 {
   169  	x1 := (int32(rawTemp) - int32(d.calCoeffs.ac6)) * int32(d.calCoeffs.ac5) >> 15
   170  	x2 := int32(d.calCoeffs.mc) << 11 / (x1 + int32(d.calCoeffs.md))
   171  	return x1 + x2
   172  }
   173  
   174  func (d *BMP180Driver) rawPressure(oversampling BMP180OversamplingMode) (rawPressure int32, err error) {
   175  	if _, err = d.connection.Write([]byte{bmp180RegisterCtl, bmp180CtlPressure + byte(oversampling<<6)}); err != nil {
   176  		return 0, err
   177  	}
   178  	time.Sleep(bmp180PauseForReading(oversampling))
   179  	ret := make([]byte, 3)
   180  	if err = d.connection.ReadBlockData(bmp180RegisterDataMSB, ret); err != nil {
   181  		return 0, err
   182  	}
   183  	rawPressure = (int32(ret[0])<<16 + int32(ret[1])<<8 + int32(ret[2])) >> (8 - uint(oversampling))
   184  	return rawPressure, nil
   185  }
   186  
   187  func (d *BMP180Driver) calculatePressure(rawTemp int16, rawPressure int32, oversampling BMP180OversamplingMode) float32 {
   188  	b5 := d.calculateB5(rawTemp)
   189  	b6 := b5 - 4000
   190  	x1 := (int32(d.calCoeffs.b2) * (b6 * b6 >> 12)) >> 11
   191  	x2 := (int32(d.calCoeffs.ac2) * b6) >> 11
   192  	x3 := x1 + x2
   193  	b3 := (((int32(d.calCoeffs.ac1)*4 + x3) << uint(oversampling)) + 2) >> 2
   194  	x1 = (int32(d.calCoeffs.ac3) * b6) >> 13
   195  	x2 = (int32(d.calCoeffs.b1) * ((b6 * b6) >> 12)) >> 16
   196  	x3 = ((x1 + x2) + 2) >> 2
   197  	b4 := (uint32(d.calCoeffs.ac4) * uint32(x3+32768)) >> 15
   198  	b7 := (uint32(rawPressure-b3) * (50000 >> uint(oversampling)))
   199  	var p int32
   200  	if b7 < 0x80000000 {
   201  		p = int32((b7 << 1) / b4)
   202  	} else {
   203  		p = int32((b7 / b4) << 1)
   204  	}
   205  	x1 = (p >> 8) * (p >> 8)
   206  	x1 = (x1 * 3038) >> 16
   207  	x2 = (-7357 * p) >> 16
   208  	return float32(p + ((x1 + x2 + 3791) >> 4))
   209  }
   210  
   211  func bmp180PauseForReading(oversampling BMP180OversamplingMode) time.Duration {
   212  	var d time.Duration
   213  	switch oversampling {
   214  	case BMP180UltraLowPower:
   215  		d = 5 * time.Millisecond
   216  	case BMP180Standard:
   217  		d = 8 * time.Millisecond
   218  	case BMP180HighResolution:
   219  		d = 14 * time.Millisecond
   220  	case BMP180UltraHighResolution:
   221  		d = 26 * time.Millisecond
   222  	}
   223  	return d
   224  }