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

     1  package i2c
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"log"
     7  	"math"
     8  )
     9  
    10  const bmp280Debug = true
    11  
    12  // the default address is applicable for SDO to VDD, for SDO to GND it will be 0x76
    13  // this is also true for bme280 (which using this address as well)
    14  const bmp280DefaultAddress = 0x77
    15  
    16  type BMP280PressureOversampling uint8
    17  type BMP280TemperatureOversampling uint8
    18  type BMP280IIRFilter uint8
    19  
    20  const (
    21  	bmp280RegCalib00      = 0x88 // 12 x 16 bit calibration data (T1..T3, P1..P9)
    22  	bmp280RegCtrl         = 0xF4 // data acquisition options (oversampling of temperature and pressure, power mode)
    23  	bmp280RegConf         = 0xF5 // rate, IIR-filter and interface options (SPI)
    24  	bmp280RegPressureData = 0xF7
    25  	bmp280RegTempData     = 0xFA
    26  
    27  	// bits 0, 1 of control register
    28  	bmp280CtrlPwrSleepMode   = 0x00
    29  	bmp280CtrlPwrForcedMode  = 0x01
    30  	bmp280CtrlPwrForcedMode2 = 0x02 // same function as 0x01
    31  	bmp280CtrlPwrNormalMode  = 0x03
    32  
    33  	// bits 2, 3, 4 of control register (will be shifted on write)
    34  	BMP280CtrlPressNoMeasurement  BMP280PressureOversampling = 0x00 // no measurement (value will be 0x08 0x00 0x00)
    35  	BMP280CtrlPressOversampling1  BMP280PressureOversampling = 0x01 // resolution 16 bit
    36  	BMP280CtrlPressOversampling2  BMP280PressureOversampling = 0x02 // resolution 17 bit
    37  	BMP280CtrlPressOversampling4  BMP280PressureOversampling = 0x03 // resolution 18 bit
    38  	BMP280CtrlPressOversampling8  BMP280PressureOversampling = 0x04 // resolution 19 bit
    39  	BMP280CtrlPressOversampling16 BMP280PressureOversampling = 0x05 // resolution 20 bit (same as 0x06, 0x07)
    40  
    41  	// bits 5, 6, 7 of control register (will be shifted on write)
    42  	BMP280CtrlTempNoMeasurement  BMP280TemperatureOversampling = 0x00 // no measurement (value will be 0x08 0x00 0x00)
    43  	BMP280CtrlTempOversampling1  BMP280TemperatureOversampling = 0x01 // resolution 16 bit
    44  	BMP280CtrlTempOversampling2  BMP280TemperatureOversampling = 0x02 // resolution 17 bit
    45  	BMP280CtrlTempOversampling4  BMP280TemperatureOversampling = 0x03 // resolution 18 bit
    46  	BMP280CtrlTempOversampling8  BMP280TemperatureOversampling = 0x04 // resolution 19 bit
    47  	BMP280CtrlTempOversampling16 BMP280TemperatureOversampling = 0x05 // resolution 20 bit
    48  
    49  	// bit 0 of config register
    50  	bmp280ConfSPIBit = 0x01 // if set, SPI is used
    51  
    52  	// bits 2, 3, 4 of config register (bit 1 is unused, will be shifted on write)
    53  	bmp280ConfStandBy0005 = 0x00 //	0.5 ms
    54  	bmp280ConfStandBy0625 = 0x01 //	62.5 ms
    55  	bmp280ConfStandBy0125 = 0x02 //	125 ms
    56  	bmp280ConfStandBy0250 = 0x03 //	250 ms
    57  	bmp280ConfStandBy0500 = 0x04 //	500 ms
    58  	bmp280ConfStandBy1000 = 0x05 //	1000 ms
    59  	bmp280ConfStandBy2000 = 0x06 //	2000 ms
    60  	bmp280ConfStandBy4000 = 0x07 //	4000 ms
    61  
    62  	// bits 5, 6, 7 of config register
    63  	BMP280ConfFilterOff BMP280IIRFilter = 0x00
    64  	BMP280ConfFilter2   BMP280IIRFilter = 0x01
    65  	BMP280ConfFilter4   BMP280IIRFilter = 0x02
    66  	BMP280ConfFilter8   BMP280IIRFilter = 0x03
    67  	BMP280ConfFilter16  BMP280IIRFilter = 0x04
    68  
    69  	bmp280SeaLevelPressure = 1013.25
    70  )
    71  
    72  type bmp280CalibrationCoefficients struct {
    73  	t1 uint16
    74  	t2 int16
    75  	t3 int16
    76  	p1 uint16
    77  	p2 int16
    78  	p3 int16
    79  	p4 int16
    80  	p5 int16
    81  	p6 int16
    82  	p7 int16
    83  	p8 int16
    84  	p9 int16
    85  }
    86  
    87  // BMP280Driver is a driver for the BMP280 temperature/pressure sensor
    88  type BMP280Driver struct {
    89  	*Driver
    90  	calCoeffs         *bmp280CalibrationCoefficients
    91  	ctrlPwrMode       uint8
    92  	ctrlPressOversamp BMP280PressureOversampling
    93  	ctrlTempOversamp  BMP280TemperatureOversampling
    94  	confFilter        BMP280IIRFilter
    95  }
    96  
    97  // NewBMP280Driver creates a new driver with specified i2c interface.
    98  // Params:
    99  //		c Connector - the Adaptor to use with this Driver
   100  //
   101  // Optional params:
   102  //		i2c.WithBus(int):	bus to use with this driver
   103  //		i2c.WithAddress(int):	address to use with this driver
   104  //
   105  func NewBMP280Driver(c Connector, options ...func(Config)) *BMP280Driver {
   106  	d := &BMP280Driver{
   107  		Driver:            NewDriver(c, "BMP280", bmp280DefaultAddress),
   108  		calCoeffs:         &bmp280CalibrationCoefficients{},
   109  		ctrlPwrMode:       bmp280CtrlPwrNormalMode,
   110  		ctrlPressOversamp: BMP280CtrlPressOversampling16,
   111  		ctrlTempOversamp:  BMP280CtrlTempOversampling1,
   112  		confFilter:        BMP280ConfFilterOff,
   113  	}
   114  	d.afterStart = d.initialization
   115  
   116  	for _, option := range options {
   117  		option(d)
   118  	}
   119  
   120  	// TODO: expose commands to API
   121  	return d
   122  }
   123  
   124  // WithBMP280PressureOversampling option sets the oversampling for pressure.
   125  // Valid settings are of type "BMP280PressureOversampling"
   126  func WithBMP280PressureOversampling(val BMP280PressureOversampling) func(Config) {
   127  	return func(c Config) {
   128  		if d, ok := c.(*BMP280Driver); ok {
   129  			d.ctrlPressOversamp = val
   130  		} else if bmp280Debug {
   131  			log.Printf("Trying to set pressure oversampling for non-BMP280Driver %v", c)
   132  		}
   133  	}
   134  }
   135  
   136  // WithBMP280TemperatureOversampling option sets oversampling for temperature.
   137  // Valid settings are of type "BMP280TemperatureOversampling"
   138  func WithBMP280TemperatureOversampling(val BMP280TemperatureOversampling) func(Config) {
   139  	return func(c Config) {
   140  		if d, ok := c.(*BMP280Driver); ok {
   141  			d.ctrlTempOversamp = val
   142  		} else if bmp280Debug {
   143  			log.Printf("Trying to set temperature oversampling for non-BMP280Driver %v", c)
   144  		}
   145  	}
   146  }
   147  
   148  // WithBMP280IIRFilter option sets the count of IIR filter coefficients.
   149  // Valid settings are of type "BMP280IIRFilter"
   150  func WithBMP280IIRFilter(val BMP280IIRFilter) func(Config) {
   151  	return func(c Config) {
   152  		if d, ok := c.(*BMP280Driver); ok {
   153  			d.confFilter = val
   154  		} else if bmp280Debug {
   155  			log.Printf("Trying to set IIR filter for non-BMP280Driver %v", c)
   156  		}
   157  	}
   158  }
   159  
   160  // Temperature returns the current temperature, in celsius degrees.
   161  func (d *BMP280Driver) Temperature() (temp float32, err error) {
   162  	d.mutex.Lock()
   163  	defer d.mutex.Unlock()
   164  
   165  	var rawT int32
   166  	if rawT, err = d.rawTemp(); err != nil {
   167  		return 0.0, err
   168  	}
   169  	temp, _ = d.calculateTemp(rawT)
   170  	return
   171  }
   172  
   173  // Pressure returns the current barometric pressure, in Pa
   174  func (d *BMP280Driver) Pressure() (press float32, err error) {
   175  	d.mutex.Lock()
   176  	defer d.mutex.Unlock()
   177  
   178  	var rawT, rawP int32
   179  	if rawT, err = d.rawTemp(); err != nil {
   180  		return 0.0, err
   181  	}
   182  
   183  	if rawP, err = d.rawPressure(); err != nil {
   184  		return 0.0, err
   185  	}
   186  	_, tFine := d.calculateTemp(rawT)
   187  	return d.calculatePress(rawP, tFine), nil
   188  }
   189  
   190  // Altitude returns the current altitude in meters based on the
   191  // current barometric pressure and estimated pressure at sea level.
   192  // Calculation is based on code from Adafruit BME280 library
   193  // 	https://github.com/adafruit/Adafruit_BME280_Library
   194  func (d *BMP280Driver) Altitude() (alt float32, err error) {
   195  	atmP, _ := d.Pressure()
   196  	atmP /= 100.0
   197  	alt = float32(44330.0 * (1.0 - math.Pow(float64(atmP/bmp280SeaLevelPressure), 0.1903)))
   198  
   199  	return
   200  }
   201  
   202  // initialization reads the calibration coefficients.
   203  func (d *BMP280Driver) initialization() (err error) {
   204  	coefficients := make([]byte, 24)
   205  	if err = d.connection.ReadBlockData(bmp280RegCalib00, coefficients); err != nil {
   206  		return err
   207  	}
   208  	buf := bytes.NewBuffer(coefficients)
   209  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t1)
   210  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t2)
   211  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t3)
   212  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p1)
   213  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p2)
   214  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p3)
   215  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p4)
   216  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p5)
   217  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p6)
   218  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p7)
   219  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p8)
   220  	binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p9)
   221  
   222  	ctrlReg := uint8(d.ctrlPwrMode) | uint8(d.ctrlPressOversamp)<<2 | uint8(d.ctrlTempOversamp)<<5
   223  	d.connection.WriteByteData(bmp280RegCtrl, ctrlReg)
   224  
   225  	confReg := uint8(bmp280ConfStandBy0005)<<2 | uint8(d.confFilter)<<5
   226  	d.connection.WriteByteData(bmp280RegConf, confReg & ^uint8(bmp280ConfSPIBit))
   227  
   228  	return nil
   229  }
   230  
   231  func (d *BMP280Driver) rawTemp() (temp int32, err error) {
   232  	var tp0, tp1, tp2 byte
   233  
   234  	data := make([]byte, 3)
   235  	if err = d.connection.ReadBlockData(bmp280RegTempData, data); err != nil {
   236  		return 0, err
   237  	}
   238  	buf := bytes.NewBuffer(data)
   239  	binary.Read(buf, binary.LittleEndian, &tp0)
   240  	binary.Read(buf, binary.LittleEndian, &tp1)
   241  	binary.Read(buf, binary.LittleEndian, &tp2)
   242  
   243  	temp = ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12))
   244  
   245  	return
   246  }
   247  
   248  func (d *BMP280Driver) rawPressure() (press int32, err error) {
   249  	var tp0, tp1, tp2 byte
   250  
   251  	data := make([]byte, 3)
   252  	if err = d.connection.ReadBlockData(bmp280RegPressureData, data); err != nil {
   253  		return 0, err
   254  	}
   255  	buf := bytes.NewBuffer(data)
   256  	binary.Read(buf, binary.LittleEndian, &tp0)
   257  	binary.Read(buf, binary.LittleEndian, &tp1)
   258  	binary.Read(buf, binary.LittleEndian, &tp2)
   259  
   260  	press = ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12))
   261  
   262  	return
   263  }
   264  
   265  func (d *BMP280Driver) calculateTemp(rawTemp int32) (float32, int32) {
   266  	tcvar1 := ((float32(rawTemp) / 16384.0) - (float32(d.calCoeffs.t1) / 1024.0)) * float32(d.calCoeffs.t2)
   267  	tcvar2 := (((float32(rawTemp) / 131072.0) - (float32(d.calCoeffs.t1) / 8192.0)) * ((float32(rawTemp) / 131072.0) - float32(d.calCoeffs.t1)/8192.0)) * float32(d.calCoeffs.t3)
   268  	temperatureComp := (tcvar1 + tcvar2) / 5120.0
   269  
   270  	tFine := int32(tcvar1 + tcvar2)
   271  	return temperatureComp, tFine
   272  }
   273  
   274  func (d *BMP280Driver) calculatePress(rawPress int32, tFine int32) float32 {
   275  	var var1, var2, p int64
   276  
   277  	var1 = int64(tFine) - 128000
   278  	var2 = var1 * var1 * int64(d.calCoeffs.p6)
   279  	var2 = var2 + ((var1 * int64(d.calCoeffs.p5)) << 17)
   280  	var2 = var2 + (int64(d.calCoeffs.p4) << 35)
   281  	var1 = (var1 * var1 * int64(d.calCoeffs.p3) >> 8) +
   282  		((var1 * int64(d.calCoeffs.p2)) << 12)
   283  	var1 = ((int64(1) << 47) + var1) * (int64(d.calCoeffs.p1)) >> 33
   284  
   285  	if var1 == 0 {
   286  		return 0 // avoid exception caused by division by zero
   287  	}
   288  	p = 1048576 - int64(rawPress)
   289  	p = (((p << 31) - var2) * 3125) / var1
   290  	var1 = (int64(d.calCoeffs.p9) * (p >> 13) * (p >> 13)) >> 25
   291  	var2 = (int64(d.calCoeffs.p8) * p) >> 19
   292  
   293  	p = ((p + var1 + var2) >> 8) + (int64(d.calCoeffs.p7) << 4)
   294  	return float32(p) / 256
   295  }