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

     1  // Package bmp180 provides a driver for the BMP180 digital pressure sensor
     2  // by Bosch.
     3  //
     4  // Datasheet:
     5  // https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
     6  package bmp180 // import "tinygo.org/x/drivers/bmp180"
     7  
     8  import (
     9  	"math"
    10  	"time"
    11  
    12  	"tinygo.org/x/drivers"
    13  	"tinygo.org/x/drivers/internal/legacy"
    14  )
    15  
    16  // OversamplingMode is the oversampling ratio of the pressure measurement.
    17  type OversamplingMode uint
    18  
    19  // calibrationCoefficients reads at startup and stores the calibration coefficients
    20  type calibrationCoefficients struct {
    21  	ac1 int16
    22  	ac2 int16
    23  	ac3 int16
    24  	ac4 uint16
    25  	ac5 uint16
    26  	ac6 uint16
    27  	b1  int16
    28  	b2  int16
    29  	mb  int16
    30  	mc  int16
    31  	md  int16
    32  }
    33  
    34  // Device wraps an I2C connection to a BMP180 device.
    35  type Device struct {
    36  	bus                     drivers.I2C
    37  	Address                 uint16
    38  	mode                    OversamplingMode
    39  	calibrationCoefficients calibrationCoefficients
    40  }
    41  
    42  // New creates a new BMP180 connection. The I2C bus must already be
    43  // configured.
    44  //
    45  // This function only creates the Device object, it does not initialize the device.
    46  // You must call Configure() first in order to use the device itself.
    47  func New(bus drivers.I2C) Device {
    48  	return Device{
    49  		bus:     bus,
    50  		Address: Address,
    51  		mode:    ULTRAHIGHRESOLUTION,
    52  	}
    53  }
    54  
    55  // Connected returns whether a BMP180 has been found.
    56  // It does a "who am I" request and checks the response.
    57  func (d *Device) Connected() bool {
    58  	data := []byte{0}
    59  	legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
    60  	return data[0] == CHIP_ID
    61  }
    62  
    63  // Configure sets up the device for communication and
    64  // read the calibration coefficients.
    65  func (d *Device) Configure() {
    66  	data := make([]byte, 22)
    67  	err := legacy.ReadRegister(d.bus, uint8(d.Address), AC1_MSB, data)
    68  	if err != nil {
    69  		return
    70  	}
    71  	d.calibrationCoefficients.ac1 = readInt(data[0], data[1])
    72  	d.calibrationCoefficients.ac2 = readInt(data[2], data[3])
    73  	d.calibrationCoefficients.ac3 = readInt(data[4], data[5])
    74  	d.calibrationCoefficients.ac4 = readUint(data[6], data[7])
    75  	d.calibrationCoefficients.ac5 = readUint(data[8], data[9])
    76  	d.calibrationCoefficients.ac6 = readUint(data[10], data[11])
    77  	d.calibrationCoefficients.b1 = readInt(data[12], data[13])
    78  	d.calibrationCoefficients.b2 = readInt(data[14], data[15])
    79  	d.calibrationCoefficients.mb = readInt(data[16], data[17])
    80  	d.calibrationCoefficients.mc = readInt(data[18], data[19])
    81  	d.calibrationCoefficients.md = readInt(data[20], data[21])
    82  }
    83  
    84  // ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
    85  func (d *Device) ReadTemperature() (temperature int32, err error) {
    86  	rawTemp, err := d.rawTemp()
    87  	if err != nil {
    88  		return
    89  	}
    90  	b5 := d.calculateB5(rawTemp)
    91  	t := (b5 + 8) >> 4
    92  	return 100 * t, nil
    93  }
    94  
    95  // ReadPressure returns the pressure in milli pascals (mPa).
    96  func (d *Device) ReadPressure() (pressure int32, err error) {
    97  	rawTemp, err := d.rawTemp()
    98  	if err != nil {
    99  		return
   100  	}
   101  	rawPressure, err := d.rawPressure(d.mode)
   102  	if err != nil {
   103  		return
   104  	}
   105  	b5 := d.calculateB5(rawTemp)
   106  	b6 := b5 - 4000
   107  	x1 := (int32(d.calibrationCoefficients.b2) * (b6 * b6 >> 12)) >> 11
   108  	x2 := (int32(d.calibrationCoefficients.ac2) * b6) >> 11
   109  	x3 := x1 + x2
   110  	b3 := (((int32(d.calibrationCoefficients.ac1)*4 + x3) << uint(d.mode)) + 2) >> 2
   111  	x1 = (int32(d.calibrationCoefficients.ac3) * b6) >> 13
   112  	x2 = (int32(d.calibrationCoefficients.b1) * ((b6 * b6) >> 12)) >> 16
   113  	x3 = ((x1 + x2) + 2) >> 2
   114  	b4 := (uint32(d.calibrationCoefficients.ac4) * uint32(x3+32768)) >> 15
   115  	b7 := uint32(rawPressure-b3) * (50000 >> uint(d.mode))
   116  	var p int32
   117  	if b7 < 0x80000000 {
   118  		p = int32((b7 << 1) / b4)
   119  	} else {
   120  		p = int32((b7 / b4) << 1)
   121  	}
   122  	x1 = (p >> 8) * (p >> 8)
   123  	x1 = (x1 * 3038) >> 16
   124  	x2 = (-7357 * p) >> 16
   125  	return 1000 * (p + ((x1 + x2 + 3791) >> 4)), nil
   126  }
   127  
   128  // ReadAltitude returns the current altitude in meters based on the
   129  // current barometric pressure and estimated pressure at sea level.
   130  // Calculation is based on code from Adafruit BME280 library
   131  //
   132  // https://github.com/adafruit/Adafruit_BME280_Library
   133  func (d *Device) ReadAltitude() (int32, error) {
   134  	mPa, err := d.ReadPressure()
   135  	if err != nil {
   136  		return 0, err
   137  	}
   138  	atmP := float32(mPa) / 100000
   139  
   140  	return int32(44330.0 * (1.0 - math.Pow(float64(atmP/SEALEVEL_PRESSURE), 0.1903))), nil
   141  }
   142  
   143  // rawTemp returns the sensor's raw values of the temperature
   144  func (d *Device) rawTemp() (int32, error) {
   145  	legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
   146  	time.Sleep(5 * time.Millisecond)
   147  	data := make([]byte, 2)
   148  	err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP_MSB, data)
   149  	if err != nil {
   150  		return 0, err
   151  	}
   152  	return int32(uint16(data[0])<<8 | uint16(data[1])), nil
   153  }
   154  
   155  // calculateB5 calculates intermediate value B5 as per page 15 of datasheet
   156  func (d *Device) calculateB5(rawTemp int32) int32 {
   157  	x1 := (rawTemp - int32(d.calibrationCoefficients.ac6)) * int32(d.calibrationCoefficients.ac5) >> 15
   158  	x2 := int32(d.calibrationCoefficients.mc) << 11 / (x1 + int32(d.calibrationCoefficients.md))
   159  	return x1 + x2
   160  }
   161  
   162  // rawPressure returns the sensor's raw values of the pressure
   163  func (d *Device) rawPressure(mode OversamplingMode) (int32, error) {
   164  	legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
   165  	time.Sleep(pauseForReading(mode))
   166  	data := make([]byte, 3)
   167  	err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_PRESSURE_MSB, data)
   168  	if err != nil {
   169  		return 0, err
   170  	}
   171  	rawPressure := int32((uint32(data[0])<<16 + uint32(data[1])<<8 + uint32(data[2])) >> (8 - uint(mode)))
   172  	return rawPressure, nil
   173  }
   174  
   175  // pauseForReading returns the pause duration depending on the sampling mode
   176  func pauseForReading(mode OversamplingMode) time.Duration {
   177  	var d time.Duration
   178  	switch mode {
   179  	case ULTRALOWPOWER:
   180  		d = 5 * time.Millisecond
   181  	case STANDARD:
   182  		d = 8 * time.Millisecond
   183  	case HIGHRESOLUTION:
   184  		d = 14 * time.Millisecond
   185  	case ULTRAHIGHRESOLUTION:
   186  		d = 26 * time.Millisecond
   187  	}
   188  	return d
   189  }
   190  
   191  // readInt converts two bytes to int16
   192  func readInt(msb byte, lsb byte) int16 {
   193  	return int16(uint16(msb)<<8 | uint16(lsb))
   194  }
   195  
   196  // readUint converts two bytes to uint16
   197  func readUint(msb byte, lsb byte) uint16 {
   198  	return (uint16(msb) << 8) | uint16(lsb)
   199  }