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

     1  package i2c
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"math"
     8  
     9  	"gobot.io/x/gobot"
    10  )
    11  
    12  const (
    13  	bmp388ChipID = 0x50
    14  
    15  	bmp388RegisterChipID       = 0x00
    16  	bmp388RegisterStatus       = 0x03
    17  	bmp388RegisterConfig       = 0x1F
    18  	bmp388RegisterPressureData = 0x04
    19  	bmp388RegisterTempData     = 0x07
    20  	bmp388RegisterCalib00      = 0x31
    21  	bmp388RegisterCMD          = 0x7E
    22  	// CMD 	: 0x00 nop (reserved. No command.)
    23  	//		: 0x34 extmode_en_middle
    24  	//		: 0xB0 fifo_flush (Clears all data in the FIFO, does not change FIFO_CONFIG registers)
    25  	//		: 0xB6 softreset (Triggers a reset, all user configuration settings are overwritten with their default state)
    26  	bmp388RegisterODR = 0x1D // Output Data Rates
    27  	bmp388RegisterOSR = 0x1C // Oversampling Rates
    28  
    29  	bmp388RegisterPWRCTRL = 0x1B
    30  	bmp388PWRCTRLSleep    = 0
    31  	bmp388PWRCTRLForced   = 1
    32  	bmp388PWRCTRLNormal   = 3
    33  
    34  	bmp388SeaLevelPressure = 1013.25
    35  
    36  	// IIR filter coefficients
    37  	bmp388IIRFIlterCoef0   = 0 // bypass-mode
    38  	bmp388IIRFIlterCoef1   = 1
    39  	bmp388IIRFIlterCoef3   = 2
    40  	bmp388IIRFIlterCoef7   = 3
    41  	bmp388IIRFIlterCoef15  = 4
    42  	bmp388IIRFIlterCoef31  = 5
    43  	bmp388IIRFIlterCoef63  = 6
    44  	bmp388IIRFIlterCoef127 = 7
    45  )
    46  
    47  // BMP388Accuracy accuracy type
    48  type BMP388Accuracy uint8
    49  
    50  // BMP388Accuracy accuracy modes
    51  const (
    52  	BMP388AccuracyUltraLow  BMP388Accuracy = 0 // x1 sample
    53  	BMP388AccuracyLow       BMP388Accuracy = 1 // x2 samples
    54  	BMP388AccuracyStandard  BMP388Accuracy = 2 // x4 samples
    55  	BMP388AccuracyHigh      BMP388Accuracy = 3 // x8 samples
    56  	BMP388AccuracyUltraHigh BMP388Accuracy = 4 // x16 samples
    57  	BMP388AccuracyHighest   BMP388Accuracy = 5 // x32 samples
    58  )
    59  
    60  type bmp388CalibrationCoefficients struct {
    61  	t1  float32
    62  	t2  float32
    63  	t3  float32
    64  	p1  float32
    65  	p2  float32
    66  	p3  float32
    67  	p4  float32
    68  	p5  float32
    69  	p6  float32
    70  	p7  float32
    71  	p8  float32
    72  	p9  float32
    73  	p10 float32
    74  	p11 float32
    75  }
    76  
    77  // BMP388Driver is a driver for the BMP388 temperature/pressure sensor
    78  type BMP388Driver struct {
    79  	name       string
    80  	connector  Connector
    81  	connection Connection
    82  	Config
    83  
    84  	tpc *bmp388CalibrationCoefficients
    85  }
    86  
    87  // NewBMP388Driver creates a new driver with specified i2c interface.
    88  // Params:
    89  //		conn Connector - the Adaptor to use with this Driver
    90  //
    91  // Optional params:
    92  //		i2c.WithBus(int):	bus to use with this driver
    93  //		i2c.WithAddress(int):	address to use with this driver
    94  //
    95  func NewBMP388Driver(c Connector, options ...func(Config)) *BMP388Driver {
    96  	b := &BMP388Driver{
    97  		name:      gobot.DefaultName("BMP388"),
    98  		connector: c,
    99  		Config:    NewConfig(),
   100  		tpc:       &bmp388CalibrationCoefficients{},
   101  	}
   102  
   103  	for _, option := range options {
   104  		option(b)
   105  	}
   106  
   107  	// TODO: expose commands to API
   108  	return b
   109  }
   110  
   111  // Name returns the name of the device.
   112  func (d *BMP388Driver) Name() string {
   113  	return d.name
   114  }
   115  
   116  // SetName sets the name of the device.
   117  func (d *BMP388Driver) SetName(n string) {
   118  	d.name = n
   119  }
   120  
   121  // Connection returns the connection of the device.
   122  func (d *BMP388Driver) Connection() gobot.Connection {
   123  	return d.connector.(gobot.Connection)
   124  }
   125  
   126  // Start initializes the BMP388 and loads the calibration coefficients.
   127  func (d *BMP388Driver) Start() (err error) {
   128  	var chipID uint8
   129  
   130  	bus := d.GetBusOrDefault(d.connector.GetDefaultBus())
   131  	address := d.GetAddressOrDefault(bmp180Address)
   132  
   133  	if d.connection, err = d.connector.GetConnection(address, bus); err != nil {
   134  		return err
   135  	}
   136  
   137  	if chipID, err = d.connection.ReadByteData(bmp388RegisterChipID); err != nil {
   138  		return err
   139  	}
   140  
   141  	if bmp388ChipID != chipID {
   142  		return fmt.Errorf("Incorrect BMP388 chip ID '0%x' Expected 0x%x", chipID, bmp388ChipID)
   143  	}
   144  
   145  	if err := d.initialization(); err != nil {
   146  		return err
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  // Halt halts the device.
   153  func (d *BMP388Driver) Halt() (err error) {
   154  	return nil
   155  }
   156  
   157  // Temperature returns the current temperature, in celsius degrees.
   158  func (d *BMP388Driver) Temperature(accuracy BMP388Accuracy) (temp float32, err error) {
   159  	var rawT int32
   160  
   161  	// Enable Pressure and Temperature measurement, set FORCED operating mode
   162  	var mode byte = (bmp388PWRCTRLForced << 4) | 3 // 1100|1|1 == mode|T|P
   163  	if err = d.connection.WriteByteData(bmp388RegisterPWRCTRL, mode); err != nil {
   164  		return 0, err
   165  	}
   166  
   167  	// Set Accuracy for temperature
   168  	if err = d.connection.WriteByteData(bmp388RegisterOSR, uint8(accuracy<<3)); err != nil {
   169  		return 0, err
   170  	}
   171  
   172  	if rawT, err = d.rawTemp(); err != nil {
   173  		return 0.0, err
   174  	}
   175  	temp = d.calculateTemp(rawT)
   176  	return
   177  }
   178  
   179  // Pressure returns the current barometric pressure, in Pa
   180  func (d *BMP388Driver) Pressure(accuracy BMP388Accuracy) (press float32, err error) {
   181  	var rawT, rawP int32
   182  
   183  	// Enable Pressure and Temperature measurement, set FORCED operating mode
   184  	var mode byte = (bmp388PWRCTRLForced << 4) | 3 // 1100|1|1 == mode|T|P
   185  	if err = d.connection.WriteByteData(bmp388RegisterPWRCTRL, mode); err != nil {
   186  		return 0, err
   187  	}
   188  
   189  	// Set Standard Accuracy for pressure
   190  	if err = d.connection.WriteByteData(bmp388RegisterOSR, uint8(accuracy)); err != nil {
   191  		return 0, err
   192  	}
   193  
   194  	if rawT, err = d.rawTemp(); err != nil {
   195  		return 0.0, err
   196  	}
   197  
   198  	if rawP, err = d.rawPressure(); err != nil {
   199  		return 0.0, err
   200  	}
   201  	tLin := d.calculateTemp(rawT)
   202  	return d.calculatePress(rawP, float64(tLin)), nil
   203  }
   204  
   205  // Altitude returns the current altitude in meters based on the
   206  // current barometric pressure and estimated pressure at sea level.
   207  // https://www.weather.gov/media/epz/wxcalc/pressureAltitude.pdf
   208  func (d *BMP388Driver) Altitude(accuracy BMP388Accuracy) (alt float32, err error) {
   209  	atmP, _ := d.Pressure(accuracy)
   210  	atmP /= 100.0
   211  	alt = float32(44307.0 * (1.0 - math.Pow(float64(atmP/bmp388SeaLevelPressure), 0.190284)))
   212  
   213  	return
   214  }
   215  
   216  // initialization reads the calibration coefficients.
   217  func (d *BMP388Driver) initialization() (err error) {
   218  	var (
   219  		coefficients []byte
   220  		t1           uint16
   221  		t2           uint16
   222  		t3           int8
   223  		p1           int16
   224  		p2           int16
   225  		p3           int8
   226  		p4           int8
   227  		p5           uint16
   228  		p6           uint16
   229  		p7           int8
   230  		p8           int8
   231  		p9           int16
   232  		p10          int8
   233  		p11          int8
   234  	)
   235  
   236  	if coefficients, err = d.read(bmp388RegisterCalib00, 24); err != nil {
   237  		return err
   238  	}
   239  	buf := bytes.NewBuffer(coefficients)
   240  
   241  	binary.Read(buf, binary.LittleEndian, &t1)
   242  	binary.Read(buf, binary.LittleEndian, &t2)
   243  	binary.Read(buf, binary.LittleEndian, &t3)
   244  	binary.Read(buf, binary.LittleEndian, &p1)
   245  	binary.Read(buf, binary.LittleEndian, &p2)
   246  	binary.Read(buf, binary.LittleEndian, &p3)
   247  	binary.Read(buf, binary.LittleEndian, &p4)
   248  	binary.Read(buf, binary.LittleEndian, &p5)
   249  	binary.Read(buf, binary.LittleEndian, &p6)
   250  	binary.Read(buf, binary.LittleEndian, &p7)
   251  	binary.Read(buf, binary.LittleEndian, &p8)
   252  	binary.Read(buf, binary.LittleEndian, &p9)
   253  	binary.Read(buf, binary.LittleEndian, &p10)
   254  	binary.Read(buf, binary.LittleEndian, &p11)
   255  
   256  	d.tpc.t1 = float32(float64(t1) / math.Pow(2, -8))
   257  	d.tpc.t2 = float32(float64(t2) / math.Pow(2, 30))
   258  	d.tpc.t3 = float32(float64(t3) / math.Pow(2, 48))
   259  	d.tpc.p1 = float32((float64(p1) - math.Pow(2, 14)) / math.Pow(2, 20))
   260  	d.tpc.p2 = float32((float64(p2) - math.Pow(2, 14)) / math.Pow(2, 29))
   261  	d.tpc.p3 = float32(float64(p3) / math.Pow(2, 32))
   262  	d.tpc.p4 = float32(float64(p4) / math.Pow(2, 37))
   263  	d.tpc.p5 = float32(float64(p5) / math.Pow(2, -3))
   264  	d.tpc.p6 = float32(float64(p6) / math.Pow(2, 6))
   265  	d.tpc.p7 = float32(float64(p7) / math.Pow(2, 8))
   266  	d.tpc.p8 = float32(float64(p8) / math.Pow(2, 15))
   267  	d.tpc.p9 = float32(float64(p9) / math.Pow(2, 48))
   268  	d.tpc.p10 = float32(float64(p10) / math.Pow(2, 48))
   269  	d.tpc.p11 = float32(float64(p11) / math.Pow(2, 65))
   270  
   271  	// Perform a power on reset. All user configuration settings are overwritten
   272  	// with their default state.
   273  	if err = d.connection.WriteByteData(bmp388RegisterCMD, 0xB6); err != nil {
   274  		return err
   275  	}
   276  
   277  	//  set IIR filter to off
   278  	if err = d.connection.WriteByteData(bmp388RegisterConfig, bmp388IIRFIlterCoef0<<1); err != nil {
   279  		return err
   280  	}
   281  
   282  	return nil
   283  }
   284  
   285  func (d *BMP388Driver) rawTemp() (temp int32, err error) {
   286  	var data []byte
   287  	var tp0, tp1, tp2 byte
   288  
   289  	if data, err = d.read(bmp388RegisterTempData, 3); err != nil {
   290  		return 0, err
   291  	}
   292  	buf := bytes.NewBuffer(data)
   293  
   294  	binary.Read(buf, binary.LittleEndian, &tp0) // XLSB
   295  	binary.Read(buf, binary.LittleEndian, &tp1) // LSB
   296  	binary.Read(buf, binary.LittleEndian, &tp2) // MSB
   297  
   298  	temp = ((int32(tp2) << 16) | (int32(tp1) << 8) | int32(tp0))
   299  	return
   300  }
   301  
   302  func (d *BMP388Driver) rawPressure() (press int32, err error) {
   303  	var data []byte
   304  	var tp0, tp1, tp2 byte
   305  
   306  	if data, err = d.read(bmp388RegisterPressureData, 3); err != nil {
   307  		return 0, err
   308  	}
   309  	buf := bytes.NewBuffer(data)
   310  
   311  	binary.Read(buf, binary.LittleEndian, &tp0) // XLSB
   312  	binary.Read(buf, binary.LittleEndian, &tp1) // LSB
   313  	binary.Read(buf, binary.LittleEndian, &tp2) // MSB
   314  
   315  	press = ((int32(tp2) << 16) | (int32(tp1) << 8) | int32(tp0))
   316  
   317  	return
   318  }
   319  
   320  func (d *BMP388Driver) calculateTemp(rawTemp int32) float32 {
   321  	// datasheet, sec 9.2 Temperature compensation
   322  	pd1 := float32(rawTemp) - d.tpc.t1
   323  	pd2 := pd1 * d.tpc.t2
   324  
   325  	temperatureComp := pd2 + (pd1*pd1)*d.tpc.t3
   326  
   327  	return temperatureComp
   328  }
   329  
   330  func (d *BMP388Driver) calculatePress(rawPress int32, tLin float64) float32 {
   331  	pd1 := float64(d.tpc.p6) * tLin
   332  	pd2 := float64(d.tpc.p7) * math.Pow(tLin, 2)
   333  	pd3 := float64(d.tpc.p8) * math.Pow(tLin, 3)
   334  	po1 := float64(d.tpc.p5) + pd1 + pd2 + pd3
   335  
   336  	pd1 = float64(d.tpc.p2) * tLin
   337  	pd2 = float64(d.tpc.p3) * math.Pow(tLin, 2)
   338  	pd3 = float64(d.tpc.p4) * math.Pow(tLin, 3)
   339  	po2 := float64(rawPress) * (float64(d.tpc.p1) + pd1 + pd2 + pd3)
   340  
   341  	pd1 = math.Pow(float64(rawPress), 2)
   342  	pd2 = float64(d.tpc.p9) + float64(d.tpc.p10)*tLin
   343  	pd3 = pd1 * pd2
   344  	pd4 := pd3 + math.Pow(float64(rawPress), 3)*float64(d.tpc.p11)
   345  
   346  	pressure := po1 + po2 + pd4
   347  
   348  	return float32(pressure)
   349  }
   350  
   351  func (d *BMP388Driver) read(address byte, n int) ([]byte, error) {
   352  	if _, err := d.connection.Write([]byte{address}); err != nil {
   353  		return nil, err
   354  	}
   355  	buf := make([]byte, n)
   356  	bytesRead, err := d.connection.Read(buf)
   357  	if bytesRead != n || err != nil {
   358  		return nil, err
   359  	}
   360  	return buf, nil
   361  }