mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-06 16:03:41 +00:00
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:
committed by
Ron Evans
parent
773b01e911
commit
3af2f18437
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user