mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
Added support for the Bosch BMP280 temperature and pressure sensor. (#158)
* bmp280: Added support for the Bosch BMP280 temperature and pressure sensor.
This commit is contained in:
@@ -27,6 +27,8 @@ smoke-test:
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp180/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp280/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/sram/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/time/main.go
|
||||
|
||||
@@ -52,7 +52,7 @@ func main() {
|
||||
|
||||
## Currently supported devices
|
||||
|
||||
The following 49 devices are supported.
|
||||
The following 50 devices are supported.
|
||||
|
||||
| Device Name | Interface Type |
|
||||
|----------|-------------|
|
||||
@@ -66,6 +66,7 @@ The following 49 devices are supported.
|
||||
| [BlinkM RGB LED](http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf) | I2C |
|
||||
| [BME280 humidity/pressure sensor](https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf) | I2C |
|
||||
| [BMP180 barometer](https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf) | I2C |
|
||||
| [BMP280 temperature/barometer](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) | I2C |
|
||||
| [Buzzer](https://en.wikipedia.org/wiki/Buzzer#Piezoelectric) | GPIO |
|
||||
| [DS1307 real time clock](https://datasheets.maximintegrated.com/en/ds/DS1307.pdf) | I2C |
|
||||
| [DS3231 real time clock](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) | I2C |
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
package bmp280
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
// OversamplingMode is the oversampling ratio of the temperature or pressure measurement.
|
||||
type Oversampling uint
|
||||
|
||||
// Mode is the Power Mode.
|
||||
type Mode uint
|
||||
|
||||
// Standby is the inactive period between the reads when the sensor is in normal power mode.
|
||||
type Standby uint
|
||||
|
||||
// Filter unwanted changes in measurement caused by external (environmental) or internal changes (IC).
|
||||
type Filter uint
|
||||
|
||||
// Device wraps an I2C connection to a BMP280 device.
|
||||
type Device struct {
|
||||
bus machine.I2C
|
||||
Address uint16
|
||||
cali calibrationCoefficients
|
||||
Temperature Oversampling
|
||||
Pressure Oversampling
|
||||
Mode Mode
|
||||
Standby Standby
|
||||
Filter Filter
|
||||
}
|
||||
|
||||
type calibrationCoefficients struct {
|
||||
// Temperature compensation
|
||||
t1 uint16
|
||||
t2 int16
|
||||
t3 int16
|
||||
|
||||
// Pressure compensation
|
||||
p1 uint16
|
||||
p2 int16
|
||||
p3 int16
|
||||
p4 int16
|
||||
p5 int16
|
||||
p6 int16
|
||||
p7 int16
|
||||
p8 int16
|
||||
p9 int16
|
||||
}
|
||||
|
||||
// New creates a new BMP280 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not initialize the device.
|
||||
// You must call Configure() first in order to use the device itself.
|
||||
func New(bus machine.I2C) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
Address: Address,
|
||||
}
|
||||
}
|
||||
|
||||
// Connected returns whether a BMP280 has been found.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := make([]byte, 1)
|
||||
d.bus.ReadRegister(uint8(d.Address), REG_ID, data)
|
||||
return data[0] == CHIP_ID
|
||||
}
|
||||
|
||||
// Reset preforms complete power-on-reset procedure.
|
||||
// It is required to call Configure afterwards.
|
||||
func (d *Device) Reset() {
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_RESET, []byte{CMD_RESET})
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication and
|
||||
// read the calibration coefficients.
|
||||
func (d *Device) Configure(standby Standby, filter Filter, temp Oversampling, pres Oversampling, mode Mode) {
|
||||
d.Standby = standby
|
||||
d.Filter = filter
|
||||
d.Temperature = temp
|
||||
d.Pressure = pres
|
||||
d.Mode = mode
|
||||
|
||||
// Write the configuration (standby, filter, spi 3 wire)
|
||||
config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CONFIG, []byte{byte(config)})
|
||||
|
||||
// Write the control (temperature oversampling, pressure oversampling,
|
||||
config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode)
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
||||
|
||||
// Read Calibration data
|
||||
data := make([]byte, 24)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CALI, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Datasheet: 3.11.2 Trimming parameter readout
|
||||
d.cali.t1 = readUintLE(data[0], data[1])
|
||||
d.cali.t2 = readIntLE(data[2], data[3])
|
||||
d.cali.t3 = readIntLE(data[4], data[5])
|
||||
|
||||
d.cali.p1 = readUintLE(data[6], data[7])
|
||||
d.cali.p2 = readIntLE(data[8], data[9])
|
||||
d.cali.p3 = readIntLE(data[10], data[11])
|
||||
d.cali.p4 = readIntLE(data[12], data[13])
|
||||
d.cali.p5 = readIntLE(data[14], data[15])
|
||||
d.cali.p6 = readIntLE(data[16], data[17])
|
||||
d.cali.p7 = readIntLE(data[18], data[19])
|
||||
d.cali.p8 = readIntLE(data[20], data[21])
|
||||
d.cali.p9 = readIntLE(data[22], data[23])
|
||||
}
|
||||
|
||||
// PrintCali prints the Calibration information.
|
||||
func (d *Device) PrintCali() {
|
||||
println("T1:", d.cali.t1)
|
||||
println("T2:", d.cali.t2)
|
||||
println("T3:", d.cali.t3)
|
||||
|
||||
println("P1:", d.cali.p1)
|
||||
println("P2:", d.cali.p2)
|
||||
println("P3:", d.cali.p3)
|
||||
println("P4:", d.cali.p4)
|
||||
println("P5:", d.cali.p5)
|
||||
println("P6:", d.cali.p6)
|
||||
println("P7:", d.cali.p7)
|
||||
println("P8:", d.cali.p8)
|
||||
println("P9:", d.cali.p9, "\n")
|
||||
}
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
|
||||
func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
data, err := d.readData(REG_TEMP, 3)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rawTemp := convert3Bytes(data[0], data[1], data[2])
|
||||
|
||||
// Datasheet: 8.2 Compensation formula in 32 bit fixed point
|
||||
// Temperature compensation
|
||||
var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11
|
||||
var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) *
|
||||
int32(d.cali.t3) >> 14
|
||||
|
||||
tFine := var1 + var2
|
||||
|
||||
// Convert from degrees to milli degrees by multiplying by 10.
|
||||
// Will output 30250 milli degrees celsius for 30.25 degrees celsius
|
||||
temperature = 10 * ((tFine*5 + 128) >> 8)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadPressure returns the pressure in milli pascals (mPa).
|
||||
func (d *Device) ReadPressure() (pressure int32, err error) {
|
||||
// First 3 bytes are Pressure, last 3 bytes are Temperature
|
||||
data, err := d.readData(REG_PRES, 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rawTemp := convert3Bytes(data[3], data[4], data[5])
|
||||
|
||||
// Datasheet: 8.2 Compensation formula in 32 bit fixed point
|
||||
// Calculate tFine (temperature), used for the Pressure compensation
|
||||
var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11
|
||||
var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) *
|
||||
int32(d.cali.t3) >> 14
|
||||
|
||||
tFine := var1 + var2
|
||||
|
||||
rawPres := convert3Bytes(data[0], data[1], data[2])
|
||||
|
||||
// Datasheet: 8.2 Compensation formula in 32 bit fixed point
|
||||
// Pressure compensation
|
||||
var1 = (tFine >> 1) - 64000
|
||||
var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * int32(d.cali.p6)
|
||||
var2 = var2 + ((var1 * int32(d.cali.p5)) << 1)
|
||||
var2 = (var2 >> 2) + (int32(d.cali.p4) << 16)
|
||||
var1 = (((int32(d.cali.p3) * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) +
|
||||
((int32(d.cali.p2) * var1) >> 1)) >> 18
|
||||
var1 = ((32768 + var1) * int32(d.cali.p1)) >> 15
|
||||
|
||||
if var1 == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
p := uint32(((1048576 - rawPres) - (var2 >> 12)) * 3125)
|
||||
if p < 0x80000000 {
|
||||
p = (p << 1) / uint32(var1)
|
||||
} else {
|
||||
p = (p / uint32(var1)) * 2
|
||||
}
|
||||
|
||||
var1 = (int32(d.cali.p9) * int32(((p>>3)*(p>>3))>>13)) >> 12
|
||||
var2 = (int32(p>>2) * int32(d.cali.p8)) >> 13
|
||||
|
||||
return 1000 * (int32(p) + ((var1 + var2 + int32(d.cali.p7)) >> 4)), nil
|
||||
}
|
||||
|
||||
// readData reads n number of bytes of the specified register
|
||||
func (d *Device) readData(register int, n int) ([]byte, error) {
|
||||
// If not in normal mode, set the mode to FORCED mode, to prevent incorrect measurements
|
||||
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
|
||||
if d.Mode != MODE_NORMAL {
|
||||
config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED)
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
||||
}
|
||||
|
||||
// Check STATUS register, wait if data is not available yet
|
||||
status := make([]byte, 1)
|
||||
for d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]) {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
// Read the requested register
|
||||
data := make([]byte, n)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), uint8(register), data[:])
|
||||
return data, err
|
||||
}
|
||||
|
||||
// convert3Bytes converts three bytes to int32
|
||||
func convert3Bytes(msb byte, b1 byte, lsb byte) int32 {
|
||||
return int32(((((uint32(msb) << 8) | uint32(b1)) << 8) | uint32(lsb)) >> 4)
|
||||
}
|
||||
|
||||
// readUint converts two bytes to uint16
|
||||
func readUint(msb byte, lsb byte) uint16 {
|
||||
return (uint16(msb) << 8) | uint16(lsb)
|
||||
}
|
||||
|
||||
// readUintLE converts two little endian bytes to uint16
|
||||
func readUintLE(msb byte, lsb byte) uint16 {
|
||||
temp := readUint(msb, lsb)
|
||||
return (temp >> 8) | (temp << 8)
|
||||
}
|
||||
|
||||
// readIntLE converts two little endian bytes to int16
|
||||
func readIntLE(msb byte, lsb byte) int16 {
|
||||
return int16(readUintLE(msb, lsb))
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Package bmp280 provides a driver for the BMP280 digital temperature & pressure sensor by Bosch.
|
||||
//
|
||||
// Datasheet: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf
|
||||
package bmp280
|
||||
|
||||
// The I2C address which this device listens to.
|
||||
const Address = 0x77
|
||||
|
||||
// Registers
|
||||
const (
|
||||
REG_ID = 0xD0 // WHO_AM_I
|
||||
REG_RESET = 0xE0
|
||||
REG_STATUS = 0xF3
|
||||
REG_CTRL_MEAS = 0xF4
|
||||
REG_CONFIG = 0xF5
|
||||
REG_TEMP = 0xFA
|
||||
REG_PRES = 0xF7
|
||||
REG_CALI = 0x88
|
||||
|
||||
CHIP_ID = 0x58
|
||||
CMD_RESET = 0xB6
|
||||
)
|
||||
|
||||
const (
|
||||
SAMPLING_SKIPPED Oversampling = iota
|
||||
SAMPLING_1X
|
||||
SAMPLING_2X
|
||||
SAMPLING_4X
|
||||
SAMPLING_8X
|
||||
SAMPLING_16X
|
||||
)
|
||||
|
||||
const (
|
||||
MODE_SLEEP Mode = 0x00
|
||||
MODE_FORCED Mode = 0x01
|
||||
MODE_NORMAL Mode = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
STANDBY_1MS Standby = iota
|
||||
STANDBY_63MS
|
||||
STANDBY_125MS
|
||||
STANDBY_250MS
|
||||
STANDBY_500MS
|
||||
STANDBY_1000MS
|
||||
STANDBY_2000MS
|
||||
STANDBY_4000MS
|
||||
)
|
||||
|
||||
const (
|
||||
FILTER_OFF Filter = iota
|
||||
FILTER_2X
|
||||
FILTER_4X
|
||||
FILTER_8X
|
||||
FILTER_16X
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
"tinygo.org/x/drivers/bmp280"
|
||||
)
|
||||
|
||||
func main() {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
sensor := bmp280.New(machine.I2C0)
|
||||
sensor.Configure(bmp280.STANDBY_125MS, bmp280.FILTER_4X, bmp280.SAMPLING_16X, bmp280.SAMPLING_16X, bmp280.MODE_FORCED)
|
||||
|
||||
connected := sensor.Connected()
|
||||
if !connected {
|
||||
println("\nBMP280 Sensor not detected\n")
|
||||
return
|
||||
}
|
||||
println("\nBMP280 Sensor detected\n")
|
||||
|
||||
println("Calibration:")
|
||||
sensor.PrintCali()
|
||||
|
||||
for {
|
||||
t, err := sensor.ReadTemperature()
|
||||
if err != nil {
|
||||
println("Error reading temperature")
|
||||
}
|
||||
// Temperature in degrees Celsius
|
||||
fmt.Printf("Temperature: %.2f °C\n", float32(t)/1000)
|
||||
|
||||
p, err := sensor.ReadPressure()
|
||||
if err != nil {
|
||||
println("Error reading pressure")
|
||||
}
|
||||
// Pressure in hectoPascal
|
||||
fmt.Printf("Pressure: %.2f hPa\n", float32(p)/100000)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user