tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/bmp280/bmp280.go (about)

     1  package bmp280
     2  
     3  import (
     4  	"time"
     5  
     6  	"tinygo.org/x/drivers"
     7  	"tinygo.org/x/drivers/internal/legacy"
     8  )
     9  
    10  // OversamplingMode is the oversampling ratio of the temperature or pressure measurement.
    11  type Oversampling uint
    12  
    13  // Mode is the Power Mode.
    14  type Mode uint
    15  
    16  // Standby is the inactive period between the reads when the sensor is in normal power mode.
    17  type Standby uint
    18  
    19  // Filter unwanted changes in measurement caused by external (environmental) or internal changes (IC).
    20  type Filter uint
    21  
    22  // Device wraps an I2C connection to a BMP280 device.
    23  type Device struct {
    24  	bus         drivers.I2C
    25  	Address     uint16
    26  	cali        calibrationCoefficients
    27  	Temperature Oversampling
    28  	Pressure    Oversampling
    29  	Mode        Mode
    30  	Standby     Standby
    31  	Filter      Filter
    32  }
    33  
    34  type calibrationCoefficients struct {
    35  	// Temperature compensation
    36  	t1 uint16
    37  	t2 int16
    38  	t3 int16
    39  
    40  	// Pressure compensation
    41  	p1 uint16
    42  	p2 int16
    43  	p3 int16
    44  	p4 int16
    45  	p5 int16
    46  	p6 int16
    47  	p7 int16
    48  	p8 int16
    49  	p9 int16
    50  }
    51  
    52  // New creates a new BMP280 connection. The I2C bus must already be
    53  // configured.
    54  //
    55  // This function only creates the Device object, it does not initialize the device.
    56  // You must call Configure() first in order to use the device itself.
    57  func New(bus drivers.I2C) Device {
    58  	return Device{
    59  		bus:     bus,
    60  		Address: Address,
    61  	}
    62  }
    63  
    64  // Connected returns whether a BMP280 has been found.
    65  // It does a "who am I" request and checks the response.
    66  func (d *Device) Connected() bool {
    67  	data := make([]byte, 1)
    68  	legacy.ReadRegister(d.bus, uint8(d.Address), REG_ID, data)
    69  	return data[0] == CHIP_ID
    70  }
    71  
    72  // Reset preforms complete power-on-reset procedure.
    73  // It is required to call Configure afterwards.
    74  func (d *Device) Reset() {
    75  	legacy.WriteRegister(d.bus, uint8(d.Address), REG_RESET, []byte{CMD_RESET})
    76  }
    77  
    78  // Configure sets up the device for communication and
    79  // read the calibration coefficients.
    80  func (d *Device) Configure(standby Standby, filter Filter, temp Oversampling, pres Oversampling, mode Mode) {
    81  	d.Standby = standby
    82  	d.Filter = filter
    83  	d.Temperature = temp
    84  	d.Pressure = pres
    85  	d.Mode = mode
    86  
    87  	//  Write the configuration (standby, filter, spi 3 wire)
    88  	config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00
    89  	legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONFIG, []byte{byte(config)})
    90  
    91  	// Write the control (temperature oversampling, pressure oversampling,
    92  	config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode)
    93  	legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
    94  
    95  	// Read Calibration data
    96  	data := make([]byte, 24)
    97  	err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALI, data)
    98  	if err != nil {
    99  		return
   100  	}
   101  
   102  	// Datasheet: 3.11.2 Trimming parameter readout
   103  	d.cali.t1 = readUintLE(data[0], data[1])
   104  	d.cali.t2 = readIntLE(data[2], data[3])
   105  	d.cali.t3 = readIntLE(data[4], data[5])
   106  
   107  	d.cali.p1 = readUintLE(data[6], data[7])
   108  	d.cali.p2 = readIntLE(data[8], data[9])
   109  	d.cali.p3 = readIntLE(data[10], data[11])
   110  	d.cali.p4 = readIntLE(data[12], data[13])
   111  	d.cali.p5 = readIntLE(data[14], data[15])
   112  	d.cali.p6 = readIntLE(data[16], data[17])
   113  	d.cali.p7 = readIntLE(data[18], data[19])
   114  	d.cali.p8 = readIntLE(data[20], data[21])
   115  	d.cali.p9 = readIntLE(data[22], data[23])
   116  }
   117  
   118  // PrintCali prints the Calibration information.
   119  func (d *Device) PrintCali() {
   120  	println("T1:", d.cali.t1)
   121  	println("T2:", d.cali.t2)
   122  	println("T3:", d.cali.t3)
   123  
   124  	println("P1:", d.cali.p1)
   125  	println("P2:", d.cali.p2)
   126  	println("P3:", d.cali.p3)
   127  	println("P4:", d.cali.p4)
   128  	println("P5:", d.cali.p5)
   129  	println("P6:", d.cali.p6)
   130  	println("P7:", d.cali.p7)
   131  	println("P8:", d.cali.p8)
   132  	println("P9:", d.cali.p9, "\n")
   133  }
   134  
   135  // ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
   136  func (d *Device) ReadTemperature() (temperature int32, err error) {
   137  	data, err := d.readData(REG_TEMP, 3)
   138  	if err != nil {
   139  		return
   140  	}
   141  
   142  	rawTemp := convert3Bytes(data[0], data[1], data[2])
   143  
   144  	// Datasheet: 8.2 Compensation formula in 32 bit fixed point
   145  	// Temperature compensation
   146  	var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11
   147  	var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) *
   148  		int32(d.cali.t3) >> 14
   149  
   150  	tFine := var1 + var2
   151  
   152  	// Convert from degrees to milli degrees by multiplying by 10.
   153  	// Will output 30250 milli degrees celsius for 30.25 degrees celsius
   154  	temperature = 10 * ((tFine*5 + 128) >> 8)
   155  	return
   156  }
   157  
   158  // ReadPressure returns the pressure in milli pascals (mPa).
   159  func (d *Device) ReadPressure() (pressure int32, err error) {
   160  	// First 3 bytes are Pressure, last 3 bytes are Temperature
   161  	data, err := d.readData(REG_PRES, 6)
   162  	if err != nil {
   163  		return
   164  	}
   165  
   166  	rawTemp := convert3Bytes(data[3], data[4], data[5])
   167  
   168  	// Datasheet: 8.2 Compensation formula in 32 bit fixed point
   169  	// Calculate tFine (temperature), used for the Pressure compensation
   170  	var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11
   171  	var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) *
   172  		int32(d.cali.t3) >> 14
   173  
   174  	tFine := var1 + var2
   175  
   176  	rawPres := convert3Bytes(data[0], data[1], data[2])
   177  
   178  	// Datasheet: 8.2 Compensation formula in 32 bit fixed point
   179  	// Pressure compensation
   180  	var1 = (tFine >> 1) - 64000
   181  	var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * int32(d.cali.p6)
   182  	var2 = var2 + ((var1 * int32(d.cali.p5)) << 1)
   183  	var2 = (var2 >> 2) + (int32(d.cali.p4) << 16)
   184  	var1 = (((int32(d.cali.p3) * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) +
   185  		((int32(d.cali.p2) * var1) >> 1)) >> 18
   186  	var1 = ((32768 + var1) * int32(d.cali.p1)) >> 15
   187  
   188  	if var1 == 0 {
   189  		return 0, nil
   190  	}
   191  
   192  	p := uint32(((1048576 - rawPres) - (var2 >> 12)) * 3125)
   193  	if p < 0x80000000 {
   194  		p = (p << 1) / uint32(var1)
   195  	} else {
   196  		p = (p / uint32(var1)) * 2
   197  	}
   198  
   199  	var1 = (int32(d.cali.p9) * int32(((p>>3)*(p>>3))>>13)) >> 12
   200  	var2 = (int32(p>>2) * int32(d.cali.p8)) >> 13
   201  
   202  	return 1000 * (int32(p) + ((var1 + var2 + int32(d.cali.p7)) >> 4)), nil
   203  }
   204  
   205  // readData reads n number of bytes of the specified register
   206  func (d *Device) readData(register int, n int) ([]byte, error) {
   207  	// If not in normal mode, set the mode to FORCED mode, to prevent incorrect measurements
   208  	// After the measurement in FORCED mode, the sensor will return to SLEEP mode
   209  	if d.Mode != MODE_NORMAL {
   210  		config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED)
   211  		legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
   212  	}
   213  
   214  	// Check STATUS register, wait if data is not available yet
   215  	status := make([]byte, 1)
   216  	for legacy.ReadRegister(d.bus, uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; legacy.ReadRegister(d.bus, uint8(d.Address), uint8(REG_STATUS), status[0:]) {
   217  		time.Sleep(time.Millisecond)
   218  	}
   219  
   220  	// Read the requested register
   221  	data := make([]byte, n)
   222  	err := legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
   223  	return data, err
   224  }
   225  
   226  // convert3Bytes converts three bytes to int32
   227  func convert3Bytes(msb byte, b1 byte, lsb byte) int32 {
   228  	return int32(((((uint32(msb) << 8) | uint32(b1)) << 8) | uint32(lsb)) >> 4)
   229  }
   230  
   231  // readUint converts two bytes to uint16
   232  func readUint(msb byte, lsb byte) uint16 {
   233  	return (uint16(msb) << 8) | uint16(lsb)
   234  }
   235  
   236  // readUintLE converts two little endian bytes to uint16
   237  func readUintLE(msb byte, lsb byte) uint16 {
   238  	temp := readUint(msb, lsb)
   239  	return (temp >> 8) | (temp << 8)
   240  }
   241  
   242  // readIntLE converts two little endian bytes to int16
   243  func readIntLE(msb byte, lsb byte) int16 {
   244  	return int16(readUintLE(msb, lsb))
   245  }