Initial support for BMP180 digital pressure sensor (#11)

* Initial support for BMP180 digital pressure sensor
Note that Pressure & Temperature return int32 and millis unit (millidegrees
and mPa)
This commit is contained in:
Daniel Esteban
2019-02-06 08:50:40 +01:00
committed by Ron Evans
parent 773b01e911
commit 3af2f18437
3 changed files with 241 additions and 0 deletions
+180
View File
@@ -0,0 +1,180 @@
// Package bmp180 provides a driver for the BMP180 digital pressure sensor
// by Bosch
//
// Datasheet:
// https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
//
package bmp180
import (
"time"
"machine"
)
// BMP180OversamplingMode is the oversampling ratio of the pressure measurement.
type OversamplingMode uint
// calibrationCoefficients reads at startup and stores the calibration coefficients
type calibrationCoefficients struct {
ac1 int16
ac2 int16
ac3 int16
ac4 uint16
ac5 uint16
ac6 uint16
b1 int16
b2 int16
mb int16
mc int16
md int16
}
// Device wraps an I2C connection to a BMP180 device.
type Device struct {
bus machine.I2C
mode OversamplingMode
calibrationCoefficients calibrationCoefficients
}
// New creates a new BMP180 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
mode: ULTRAHIGHRESOLUTION,
}
}
// Connected returns whether a BMP180 has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(Address, WHO_AM_I, data)
return data[0] == CHIP_ID
}
// Configure sets up the device for communication and
// read the calibration coefficientes.
func (d *Device) Configure() {
data := make([]byte, 22)
err := d.bus.ReadRegister(Address, AC1_MSB, data)
if err != nil {
return
}
d.calibrationCoefficients.ac1 = readInt(data[0], data[1])
d.calibrationCoefficients.ac2 = readInt(data[2], data[3])
d.calibrationCoefficients.ac3 = readInt(data[4], data[5])
d.calibrationCoefficients.ac4 = readUint(data[6], data[7])
d.calibrationCoefficients.ac5 = readUint(data[8], data[9])
d.calibrationCoefficients.ac6 = readUint(data[10], data[11])
d.calibrationCoefficients.b1 = readInt(data[12], data[13])
d.calibrationCoefficients.b2 = readInt(data[14], data[15])
d.calibrationCoefficients.mb = readInt(data[16], data[17])
d.calibrationCoefficients.mc = readInt(data[18], data[19])
d.calibrationCoefficients.md = readInt(data[20], data[21])
}
// Temperature returns the temperature in celsius milli degrees (ºC/1000)
func (d *Device) Temperature() (temperature int32, err error) {
rawTemp, err := d.rawTemp()
if err != nil {
return
}
b5 := d.calculateB5(rawTemp)
t := (b5 + 8) >> 4
return 100*t, nil
}
// Pressure returns the pressure in milli pascals (mPa)
func (d *Device) Pressure() (pressure int32, err error) {
rawTemp, err := d.rawTemp()
if err != nil {
return
}
rawPressure, err := d.rawPressure(d.mode)
if err != nil {
return
}
b5 := d.calculateB5(rawTemp)
b6 := b5 - 4000
x1 := (int32(d.calibrationCoefficients.b2) * (b6 * b6 >> 12)) >> 11
x2 := (int32(d.calibrationCoefficients.ac2) * b6) >> 11
x3 := x1 + x2
b3 := (((int32(d.calibrationCoefficients.ac1)*4 + x3) << uint(d.mode)) + 2) >> 2
x1 = (int32(d.calibrationCoefficients.ac3) * b6) >> 13
x2 = (int32(d.calibrationCoefficients.b1) * ((b6 * b6) >> 12)) >> 16
x3 = ((x1 + x2) + 2) >> 2
b4 := (uint32(d.calibrationCoefficients.ac4) * uint32(x3+32768)) >> 15
b7 := uint32(rawPressure-b3) * (50000 >> uint(d.mode))
var p int32
if b7 < 0x80000000 {
p = int32((b7 << 1) / b4)
} else {
p = int32((b7 / b4) << 1)
}
x1 = (p >> 8) * (p >> 8)
x1 = (x1 * 3038) >> 16
x2 = (-7357 * p) >> 16
return 1000*(p + ((x1 + x2 + 3791) >> 4)), nil
}
// rawTemp returns the sensor's raw values of the temperature
func (d *Device) rawTemp() (int16, error) {
d.bus.WriteRegister(Address, REG_CTRL, []byte{CMD_TEMP})
time.Sleep(5 * time.Millisecond)
data := make([]byte, 2)
err := d.bus.ReadRegister(Address, REG_TEMP_MSB, data)
if err != nil {
return 0, err
}
return readInt(data[0], data[1]), nil
}
// calculateB5 calculates intermediate value B5 as per page 15 of datasheet
func (d *Device) calculateB5(rawTemp int16) int32 {
x1 := (int32(rawTemp) - int32(d.calibrationCoefficients.ac6)) * int32(d.calibrationCoefficients.ac5) >> 15
x2 := int32(d.calibrationCoefficients.mc) << 11 / (x1 + int32(d.calibrationCoefficients.md))
return x1 + x2
}
// rawPressure returns the sensor's raw values of the pressure
func (d *Device) rawPressure(mode OversamplingMode) (int32, error) {
d.bus.WriteRegister(Address, REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
time.Sleep(pauseForReading(mode))
data := make([]byte, 3)
err := d.bus.ReadRegister(Address, REG_PRESSURE_MSB, data)
if err != nil {
return 0, err
}
rawPressure := int32((uint32(data[0])<<16 + uint32(data[1])<<8 + uint32(data[2])) >> (8 - uint(mode)))
return rawPressure, nil
}
// pauseForReading returns the pause duration depending on the sampling mode
func pauseForReading(mode OversamplingMode) time.Duration {
var d time.Duration
switch mode {
case ULTRALOWPOWER:
d = 5 * time.Millisecond
case STANDARD:
d = 8 * time.Millisecond
case HIGHRESOLUTION:
d = 14 * time.Millisecond
case ULTRAHIGHRESOLUTION:
d = 26 * time.Millisecond
}
return d
}
// readInt converts two bytes to int16
func readInt(msb byte, lsb byte) int16 {
return int16(uint16(msb)<<8 | uint16(lsb))
}
// readUint converts two bytes to uint16
func readUint(msb byte, lsb byte) uint16 {
return (uint16(msb) << 8) | uint16(lsb)
}
+30
View File
@@ -0,0 +1,30 @@
package bmp180
// Constants/addresses used for I2C.
// The I2C address which this device listens to.
const Address = 0x77
// Registers. Names, addresses and comments copied from the datasheet.
const (
AC1_MSB = 0xAA // Calibration coefficients start at 0xAA ends at 0xBF
CMD_TEMP = 0x2E
CMD_PRESSURE = 0x34
REG_CTRL = 0xF4
REG_TEMP_MSB = 0xF6
REG_PRESSURE_MSB = 0xF6
WHO_AM_I = 0xD0
CHIP_ID = 0x55
)
const (
// ULTRALOWPOWER is the lowest oversampling mode of the pressure measurement.
ULTRALOWPOWER OversamplingMode = iota
// BSTANDARD is the standard oversampling mode of the pressure measurement.
STANDARD
// HIGHRESOLUTION is a high oversampling mode of the pressure measurement.
HIGHRESOLUTION
// ULTRAHIGHRESOLUTION is the highest oversampling mode of the pressure measurement.
ULTRAHIGHRESOLUTION
)
+31
View File
@@ -0,0 +1,31 @@
package main
import (
"time"
"github.com/tinygo-org/drivers/bmp180"
"machine"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := bmp180.New(machine.I2C0)
sensor.Configure()
connected := sensor.Connected()
if !connected {
println("BMP180 not detected")
return
}
println("BMP180 detected")
for {
temp, _ := sensor.Temperature()
println("Temperature:", float32(temp)/1000, "ºC")
pressure, _ := sensor.Pressure()
println("Pressure", float32(pressure)/100000, "hPa")
time.Sleep(2 * time.Second)
}
}