gobot.io/x/gobot@v1.16.0/drivers/i2c/bmp280_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"math"
     7  
     8  	"gobot.io/x/gobot"
     9  )
    10  
    11  const (
    12  	bmp280RegisterControl      = 0xf4
    13  	bmp280RegisterConfig       = 0xf5
    14  	bmp280RegisterPressureData = 0xf7
    15  	bmp280RegisterTempData     = 0xfa
    16  	bmp280RegisterCalib00      = 0x88
    17  	bmp280SeaLevelPressure     = 1013.25
    18  )
    19  
    20  type bmp280CalibrationCoefficients struct {
    21  	t1 uint16
    22  	t2 int16
    23  	t3 int16
    24  	p1 uint16
    25  	p2 int16
    26  	p3 int16
    27  	p4 int16
    28  	p5 int16
    29  	p6 int16
    30  	p7 int16
    31  	p8 int16
    32  	p9 int16
    33  }
    34  
    35  // BMP280Driver is a driver for the BMP280 temperature/pressure sensor
    36  type BMP280Driver struct {
    37  	name       string
    38  	connector  Connector
    39  	connection Connection
    40  	Config
    41  
    42  	tpc *bmp280CalibrationCoefficients
    43  }
    44  
    45  // NewBMP280Driver creates a new driver with specified i2c interface.
    46  // Params:
    47  //		conn Connector - the Adaptor to use with this Driver
    48  //
    49  // Optional params:
    50  //		i2c.WithBus(int):	bus to use with this driver
    51  //		i2c.WithAddress(int):	address to use with this driver
    52  //
    53  func NewBMP280Driver(c Connector, options ...func(Config)) *BMP280Driver {
    54  	b := &BMP280Driver{
    55  		name:      gobot.DefaultName("BMP280"),
    56  		connector: c,
    57  		Config:    NewConfig(),
    58  		tpc:       &bmp280CalibrationCoefficients{},
    59  	}
    60  
    61  	for _, option := range options {
    62  		option(b)
    63  	}
    64  
    65  	// TODO: expose commands to API
    66  	return b
    67  }
    68  
    69  // Name returns the name of the device.
    70  func (d *BMP280Driver) Name() string {
    71  	return d.name
    72  }
    73  
    74  // SetName sets the name of the device.
    75  func (d *BMP280Driver) SetName(n string) {
    76  	d.name = n
    77  }
    78  
    79  // Connection returns the connection of the device.
    80  func (d *BMP280Driver) Connection() gobot.Connection {
    81  	return d.connector.(gobot.Connection)
    82  }
    83  
    84  // Start initializes the BMP280 and loads the calibration coefficients.
    85  func (d *BMP280Driver) Start() (err error) {
    86  	bus := d.GetBusOrDefault(d.connector.GetDefaultBus())
    87  	address := d.GetAddressOrDefault(bmp180Address)
    88  
    89  	if d.connection, err = d.connector.GetConnection(address, bus); err != nil {
    90  		return err
    91  	}
    92  
    93  	if err := d.initialization(); err != nil {
    94  		return err
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  // Halt halts the device.
   101  func (d *BMP280Driver) Halt() (err error) {
   102  	return nil
   103  }
   104  
   105  // Temperature returns the current temperature, in celsius degrees.
   106  func (d *BMP280Driver) Temperature() (temp float32, err error) {
   107  	var rawT int32
   108  	if rawT, err = d.rawTemp(); err != nil {
   109  		return 0.0, err
   110  	}
   111  	temp, _ = d.calculateTemp(rawT)
   112  	return
   113  }
   114  
   115  // Pressure returns the current barometric pressure, in Pa
   116  func (d *BMP280Driver) Pressure() (press float32, err error) {
   117  	var rawT, rawP int32
   118  	if rawT, err = d.rawTemp(); err != nil {
   119  		return 0.0, err
   120  	}
   121  
   122  	if rawP, err = d.rawPressure(); err != nil {
   123  		return 0.0, err
   124  	}
   125  	_, tFine := d.calculateTemp(rawT)
   126  	return d.calculatePress(rawP, tFine), nil
   127  }
   128  
   129  // Altitude returns the current altitude in meters based on the
   130  // current barometric pressure and estimated pressure at sea level.
   131  // Calculation is based on code from Adafruit BME280 library
   132  // 	https://github.com/adafruit/Adafruit_BME280_Library
   133  func (d *BMP280Driver) Altitude() (alt float32, err error) {
   134  	atmP, _ := d.Pressure()
   135  	atmP /= 100.0
   136  	alt = float32(44330.0 * (1.0 - math.Pow(float64(atmP/bmp280SeaLevelPressure), 0.1903)))
   137  
   138  	return
   139  }
   140  
   141  // initialization reads the calibration coefficients.
   142  func (d *BMP280Driver) initialization() (err error) {
   143  	var coefficients []byte
   144  	if coefficients, err = d.read(bmp280RegisterCalib00, 24); err != nil {
   145  		return err
   146  	}
   147  	buf := bytes.NewBuffer(coefficients)
   148  	binary.Read(buf, binary.LittleEndian, &d.tpc.t1)
   149  	binary.Read(buf, binary.LittleEndian, &d.tpc.t2)
   150  	binary.Read(buf, binary.LittleEndian, &d.tpc.t3)
   151  	binary.Read(buf, binary.LittleEndian, &d.tpc.p1)
   152  	binary.Read(buf, binary.LittleEndian, &d.tpc.p2)
   153  	binary.Read(buf, binary.LittleEndian, &d.tpc.p3)
   154  	binary.Read(buf, binary.LittleEndian, &d.tpc.p4)
   155  	binary.Read(buf, binary.LittleEndian, &d.tpc.p5)
   156  	binary.Read(buf, binary.LittleEndian, &d.tpc.p6)
   157  	binary.Read(buf, binary.LittleEndian, &d.tpc.p7)
   158  	binary.Read(buf, binary.LittleEndian, &d.tpc.p8)
   159  	binary.Read(buf, binary.LittleEndian, &d.tpc.p9)
   160  
   161  	d.connection.WriteByteData(bmp280RegisterControl, 0x3F)
   162  
   163  	return nil
   164  }
   165  
   166  func (d *BMP280Driver) rawTemp() (temp int32, err error) {
   167  	var data []byte
   168  	var tp0, tp1, tp2 byte
   169  
   170  	if data, err = d.read(bmp280RegisterTempData, 3); err != nil {
   171  		return 0, err
   172  	}
   173  	buf := bytes.NewBuffer(data)
   174  	binary.Read(buf, binary.LittleEndian, &tp0)
   175  	binary.Read(buf, binary.LittleEndian, &tp1)
   176  	binary.Read(buf, binary.LittleEndian, &tp2)
   177  
   178  	temp = ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12))
   179  
   180  	return
   181  }
   182  
   183  func (d *BMP280Driver) rawPressure() (press int32, err error) {
   184  	var data []byte
   185  	var tp0, tp1, tp2 byte
   186  
   187  	if data, err = d.read(bmp280RegisterPressureData, 3); err != nil {
   188  		return 0, err
   189  	}
   190  	buf := bytes.NewBuffer(data)
   191  	binary.Read(buf, binary.LittleEndian, &tp0)
   192  	binary.Read(buf, binary.LittleEndian, &tp1)
   193  	binary.Read(buf, binary.LittleEndian, &tp2)
   194  
   195  	press = ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12))
   196  
   197  	return
   198  }
   199  
   200  func (d *BMP280Driver) calculateTemp(rawTemp int32) (float32, int32) {
   201  	tcvar1 := ((float32(rawTemp) / 16384.0) - (float32(d.tpc.t1) / 1024.0)) * float32(d.tpc.t2)
   202  	tcvar2 := (((float32(rawTemp) / 131072.0) - (float32(d.tpc.t1) / 8192.0)) * ((float32(rawTemp) / 131072.0) - float32(d.tpc.t1)/8192.0)) * float32(d.tpc.t3)
   203  	temperatureComp := (tcvar1 + tcvar2) / 5120.0
   204  
   205  	tFine := int32(tcvar1 + tcvar2)
   206  	return temperatureComp, tFine
   207  }
   208  
   209  func (d *BMP280Driver) calculatePress(rawPress int32, tFine int32) float32 {
   210  	var var1, var2, p int64
   211  
   212  	var1 = int64(tFine) - 128000
   213  	var2 = var1 * var1 * int64(d.tpc.p6)
   214  	var2 = var2 + ((var1 * int64(d.tpc.p5)) << 17)
   215  	var2 = var2 + (int64(d.tpc.p4) << 35)
   216  	var1 = (var1 * var1 * int64(d.tpc.p3) >> 8) +
   217  		((var1 * int64(d.tpc.p2)) << 12)
   218  	var1 = ((int64(1) << 47) + var1) * (int64(d.tpc.p1)) >> 33
   219  
   220  	if var1 == 0 {
   221  		return 0 // avoid exception caused by division by zero
   222  	}
   223  	p = 1048576 - int64(rawPress)
   224  	p = (((p << 31) - var2) * 3125) / var1
   225  	var1 = (int64(d.tpc.p9) * (p >> 13) * (p >> 13)) >> 25
   226  	var2 = (int64(d.tpc.p8) * p) >> 19
   227  
   228  	p = ((p + var1 + var2) >> 8) + (int64(d.tpc.p7) << 4)
   229  	return float32(p) / 256
   230  }
   231  
   232  func (d *BMP280Driver) read(address byte, n int) ([]byte, error) {
   233  	if _, err := d.connection.Write([]byte{address}); err != nil {
   234  		return nil, err
   235  	}
   236  	buf := make([]byte, n)
   237  	bytesRead, err := d.connection.Read(buf)
   238  	if bytesRead != n || err != nil {
   239  		return nil, err
   240  	}
   241  	return buf, nil
   242  }