gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/bmp180_driver.go (about) 1 package i2c 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "log" 7 "time" 8 ) 9 10 const bmp180Debug = false 11 12 // the default address is applicable for SDO to VDD, for SDO to GND it will be 0x76 13 const bmp180DefaultAddress = 0x77 14 15 const ( 16 bmp180RegisterAC1MSB = 0xAA // 11 x 16 bit calibration data (AC1..AC6, B1, B2, MB, MC, MD) 17 bmp180RegisterCtl = 0xF4 // control the value to read 18 bmp180RegisterDataMSB = 0xF6 // 16 bit data (temperature or pressure) 19 20 bmp180CtlTemp = 0x2E 21 bmp180CtlPressure = 0x34 22 ) 23 24 const ( 25 // BMP180UltraLowPower is the lowest oversampling mode of the pressure measurement. 26 BMP180UltraLowPower BMP180OversamplingMode = iota 27 // BMP180Standard is the standard oversampling mode of the pressure measurement. 28 BMP180Standard 29 // BMP180HighResolution is a high oversampling mode of the pressure measurement. 30 BMP180HighResolution 31 // BMP180UltraHighResolution is the highest oversampling mode of the pressure measurement. 32 BMP180UltraHighResolution 33 ) 34 35 // BMP180OversamplingMode is the oversampling ratio of the pressure measurement. 36 type BMP180OversamplingMode uint 37 38 type bmp180CalibrationCoefficients struct { 39 ac1 int16 40 ac2 int16 41 ac3 int16 42 ac4 uint16 43 ac5 uint16 44 ac6 uint16 45 b1 int16 46 b2 int16 47 mb int16 48 mc int16 49 md int16 50 } 51 52 // BMP180Driver is the gobot driver for the Bosch pressure and temperature sensor BMP180. 53 // Device datasheet: https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf 54 type BMP180Driver struct { 55 *Driver 56 oversampling BMP180OversamplingMode 57 calCoeffs *bmp180CalibrationCoefficients 58 } 59 60 // NewBMP180Driver creates a new driver with the i2c interface for the BMP180 device. 61 // Params: 62 // conn Connector - the Adaptor to use with this Driver 63 // 64 // Optional params: 65 // i2c.WithBus(int): bus to use with this driver 66 // i2c.WithAddress(int): address to use with this driver 67 // 68 func NewBMP180Driver(c Connector, options ...func(Config)) *BMP180Driver { 69 d := &BMP180Driver{ 70 Driver: NewDriver(c, "BMP180", bmp180DefaultAddress), 71 oversampling: BMP180UltraLowPower, 72 calCoeffs: &bmp180CalibrationCoefficients{}, 73 } 74 d.afterStart = d.initialization 75 76 for _, option := range options { 77 option(d) 78 } 79 80 // TODO: expose commands to API 81 return d 82 } 83 84 // WithBMP180oversampling option sets oversampling mode. 85 // Valid settings are of type "BMP180OversamplingMode" 86 func WithBMP180OversamplingMode(val BMP180OversamplingMode) func(Config) { 87 return func(c Config) { 88 if d, ok := c.(*BMP180Driver); ok { 89 d.oversampling = val 90 } else if bmp180Debug { 91 log.Printf("Trying to set oversampling mode for non-BMP180Driver %v", c) 92 } 93 } 94 } 95 96 // Temperature returns the current temperature, in celsius degrees. 97 func (d *BMP180Driver) Temperature() (temp float32, err error) { 98 d.mutex.Lock() 99 defer d.mutex.Unlock() 100 101 var rawTemp int16 102 if rawTemp, err = d.rawTemp(); err != nil { 103 return 0, err 104 } 105 return d.calculateTemp(rawTemp), nil 106 } 107 108 // Pressure returns the current pressure, in pascals. 109 func (d *BMP180Driver) Pressure() (pressure float32, err error) { 110 d.mutex.Lock() 111 defer d.mutex.Unlock() 112 113 var rawTemp int16 114 var rawPressure int32 115 if rawTemp, err = d.rawTemp(); err != nil { 116 return 0, err 117 } 118 if rawPressure, err = d.rawPressure(d.oversampling); err != nil { 119 return 0, err 120 } 121 return d.calculatePressure(rawTemp, rawPressure, d.oversampling), nil 122 } 123 124 func (d *BMP180Driver) initialization() (err error) { 125 // read the 11 calibration coefficients. 126 coefficients := make([]byte, 22) 127 if err = d.connection.ReadBlockData(bmp180RegisterAC1MSB, coefficients); err != nil { 128 return err 129 } 130 buf := bytes.NewBuffer(coefficients) 131 binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac1) 132 binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac2) 133 binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac3) 134 binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac4) 135 binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac5) 136 binary.Read(buf, binary.BigEndian, &d.calCoeffs.ac6) 137 binary.Read(buf, binary.BigEndian, &d.calCoeffs.b1) 138 binary.Read(buf, binary.BigEndian, &d.calCoeffs.b2) 139 binary.Read(buf, binary.BigEndian, &d.calCoeffs.mb) 140 binary.Read(buf, binary.BigEndian, &d.calCoeffs.mc) 141 binary.Read(buf, binary.BigEndian, &d.calCoeffs.md) 142 143 return nil 144 } 145 146 func (d *BMP180Driver) rawTemp() (int16, error) { 147 if _, err := d.connection.Write([]byte{bmp180RegisterCtl, bmp180CtlTemp}); err != nil { 148 return 0, err 149 } 150 time.Sleep(5 * time.Millisecond) 151 ret := make([]byte, 2) 152 err := d.connection.ReadBlockData(bmp180RegisterDataMSB, ret) 153 if err != nil { 154 return 0, err 155 } 156 buf := bytes.NewBuffer(ret) 157 var rawTemp int16 158 binary.Read(buf, binary.BigEndian, &rawTemp) 159 return rawTemp, nil 160 } 161 162 func (d *BMP180Driver) calculateTemp(rawTemp int16) float32 { 163 b5 := d.calculateB5(rawTemp) 164 t := (b5 + 8) >> 4 165 return float32(t) / 10 166 } 167 168 func (d *BMP180Driver) calculateB5(rawTemp int16) int32 { 169 x1 := (int32(rawTemp) - int32(d.calCoeffs.ac6)) * int32(d.calCoeffs.ac5) >> 15 170 x2 := int32(d.calCoeffs.mc) << 11 / (x1 + int32(d.calCoeffs.md)) 171 return x1 + x2 172 } 173 174 func (d *BMP180Driver) rawPressure(oversampling BMP180OversamplingMode) (rawPressure int32, err error) { 175 if _, err = d.connection.Write([]byte{bmp180RegisterCtl, bmp180CtlPressure + byte(oversampling<<6)}); err != nil { 176 return 0, err 177 } 178 time.Sleep(bmp180PauseForReading(oversampling)) 179 ret := make([]byte, 3) 180 if err = d.connection.ReadBlockData(bmp180RegisterDataMSB, ret); err != nil { 181 return 0, err 182 } 183 rawPressure = (int32(ret[0])<<16 + int32(ret[1])<<8 + int32(ret[2])) >> (8 - uint(oversampling)) 184 return rawPressure, nil 185 } 186 187 func (d *BMP180Driver) calculatePressure(rawTemp int16, rawPressure int32, oversampling BMP180OversamplingMode) float32 { 188 b5 := d.calculateB5(rawTemp) 189 b6 := b5 - 4000 190 x1 := (int32(d.calCoeffs.b2) * (b6 * b6 >> 12)) >> 11 191 x2 := (int32(d.calCoeffs.ac2) * b6) >> 11 192 x3 := x1 + x2 193 b3 := (((int32(d.calCoeffs.ac1)*4 + x3) << uint(oversampling)) + 2) >> 2 194 x1 = (int32(d.calCoeffs.ac3) * b6) >> 13 195 x2 = (int32(d.calCoeffs.b1) * ((b6 * b6) >> 12)) >> 16 196 x3 = ((x1 + x2) + 2) >> 2 197 b4 := (uint32(d.calCoeffs.ac4) * uint32(x3+32768)) >> 15 198 b7 := (uint32(rawPressure-b3) * (50000 >> uint(oversampling))) 199 var p int32 200 if b7 < 0x80000000 { 201 p = int32((b7 << 1) / b4) 202 } else { 203 p = int32((b7 / b4) << 1) 204 } 205 x1 = (p >> 8) * (p >> 8) 206 x1 = (x1 * 3038) >> 16 207 x2 = (-7357 * p) >> 16 208 return float32(p + ((x1 + x2 + 3791) >> 4)) 209 } 210 211 func bmp180PauseForReading(oversampling BMP180OversamplingMode) time.Duration { 212 var d time.Duration 213 switch oversampling { 214 case BMP180UltraLowPower: 215 d = 5 * time.Millisecond 216 case BMP180Standard: 217 d = 8 * time.Millisecond 218 case BMP180HighResolution: 219 d = 14 * time.Millisecond 220 case BMP180UltraHighResolution: 221 d = 26 * time.Millisecond 222 } 223 return d 224 }