forked from google/periph
/
bmx280.go
212 lines (191 loc) · 6 KB
/
bmx280.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// Copyright 2016 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package bmxx80
import (
"time"
"periph.io/x/periph/conn/physic"
)
// sense280 reads the device's registers for bme280/bmp280.
//
// It must be called with d.mu lock held.
func (d *Dev) sense280(e *physic.Env) error {
// All registers must be read in a single pass, as noted at page 21, section
// 4.1.
// Pressure: 0xF7~0xF9
// Temperature: 0xFA~0xFC
// Humidity: 0xFD~0xFE
buf := [8]byte{}
b := buf[:]
if !d.isBME {
b = buf[:6]
}
if err := d.readReg(0xF7, b); err != nil {
return err
}
// These values are 20 bits as per doc.
pRaw := int32(buf[0])<<12 | int32(buf[1])<<4 | int32(buf[2])>>4
tRaw := int32(buf[3])<<12 | int32(buf[4])<<4 | int32(buf[5])>>4
t, tFine := d.cal280.compensateTempInt(tRaw)
// Convert CentiCelsius to Kelvin.
e.Temperature = physic.Temperature(t)*10*physic.MilliCelsius + physic.ZeroCelsius
if d.opts.Pressure != Off {
p := d.cal280.compensatePressureInt64(pRaw, tFine)
// It has 8 bits of fractional Pascal.
e.Pressure = physic.Pressure(p) * 15625 * physic.MicroPascal / 4
}
if d.opts.Humidity != Off {
// This value is 16 bits as per doc.
hRaw := int32(buf[6])<<8 | int32(buf[7])
h := physic.RelativeHumidity(d.cal280.compensateHumidityInt(hRaw, tFine))
// Convert base 1024 to base 1000.
e.Humidity = h * 10000 / 1024 * physic.MicroRH
}
return nil
}
func (d *Dev) isIdle280() (bool, error) {
// status
v := [1]byte{}
if err := d.readReg(0xF3, v[:]); err != nil {
return false, err
}
// Make sure bit 3 is cleared. Bit 0 is only important at device boot up.
return v[0]&8 == 0, nil
}
// mode is the operating mode.
type mode byte
const (
sleep mode = 0 // no operation, all registers accessible, lowest power, selected after startup
forced mode = 1 // perform one measurement, store results and return to sleep mode
normal mode = 3 // perpetual cycling of measurements and inactive periods
)
// standby is the time the BMx280 waits idle between measurements. This reduces
// power consumption when the host won't read the values as fast as the
// measurements are done.
type standby uint8
// Possible standby values, these determines the refresh rate.
const (
s500us standby = 0
s10msBME standby = 6
s20msBME standby = 7
s62ms standby = 1
s125ms standby = 2
s250ms standby = 3
s500ms standby = 4
s1s standby = 5
s2sBMP standby = 6
s4sBMP standby = 7
)
func chooseStandby(isBME bool, d time.Duration) standby {
switch {
case d < 10*time.Millisecond:
return s500us
case isBME && d < 20*time.Millisecond:
return s10msBME
case isBME && d < 62500*time.Microsecond:
return s20msBME
case d < 125*time.Millisecond:
return s62ms
case d < 250*time.Millisecond:
return s125ms
case d < 500*time.Millisecond:
return s250ms
case d < time.Second:
return s500ms
case d < 2*time.Second:
return s1s
case !isBME && d < 4*time.Second:
return s2sBMP
default:
if isBME {
return s1s
}
return s4sBMP
}
}
// newCalibration parses calibration data from both buffers.
func newCalibration(tph, h []byte) (c calibration280) {
c.t1 = uint16(tph[0]) | uint16(tph[1])<<8
c.t2 = int16(tph[2]) | int16(tph[3])<<8
c.t3 = int16(tph[4]) | int16(tph[5])<<8
c.p1 = uint16(tph[6]) | uint16(tph[7])<<8
c.p2 = int16(tph[8]) | int16(tph[9])<<8
c.p3 = int16(tph[10]) | int16(tph[11])<<8
c.p4 = int16(tph[12]) | int16(tph[13])<<8
c.p5 = int16(tph[14]) | int16(tph[15])<<8
c.p6 = int16(tph[16]) | int16(tph[17])<<8
c.p7 = int16(tph[18]) | int16(tph[19])<<8
c.p8 = int16(tph[20]) | int16(tph[21])<<8
c.p9 = int16(tph[22]) | int16(tph[23])<<8
c.h1 = uint8(tph[25])
c.h2 = int16(h[0]) | int16(h[1])<<8
c.h3 = uint8(h[2])
c.h4 = int16(h[3])<<4 | int16(h[4])&0xF
c.h5 = int16(h[4])>>4 | int16(h[5])<<4
c.h6 = int8(h[6])
return c
}
type calibration280 struct {
t1 uint16
t2, t3 int16
p1 uint16
p2, p3, p4, p5, p6, p7, p8, p9 int16
h2 int16 // Reordered for packing
h1, h3 uint8
h4, h5 int16
h6 int8
}
// Pages 23-24
// compensateTempInt returns temperature in °C, resolution is 0.01 °C.
// Output value of 5123 equals 51.23 C.
//
// raw has 20 bits of resolution.
func (c *calibration280) compensateTempInt(raw int32) (int32, int32) {
x := ((raw>>3 - int32(c.t1)<<1) * int32(c.t2)) >> 11
y := ((((raw>>4 - int32(c.t1)) * (raw>>4 - int32(c.t1))) >> 12) * int32(c.t3)) >> 14
tFine := x + y
return (tFine*5 + 128) >> 8, tFine
}
// compensatePressureInt64 returns pressure in Pa in Q24.8 format (24 integer
// bits and 8 fractional bits). Output value of 24674867 represents
// 24674867/256 = 96386.2 Pa = 963.862 hPa.
//
// raw has 20 bits of resolution.
func (c *calibration280) compensatePressureInt64(raw, tFine int32) uint32 {
x := int64(tFine) - 128000
y := x * x * int64(c.p6)
y += (x * int64(c.p5)) << 17
y += int64(c.p4) << 35
x = (x*x*int64(c.p3))>>8 + ((x * int64(c.p2)) << 12)
x = ((int64(1)<<47 + x) * int64(c.p1)) >> 33
if x == 0 {
return 0
}
p := ((((1048576 - int64(raw)) << 31) - y) * 3125) / x
x = (int64(c.p9) * (p >> 13) * (p >> 13)) >> 25
y = (int64(c.p8) * p) >> 19
return uint32(((p + x + y) >> 8) + (int64(c.p7) << 4))
}
// compensateHumidityInt returns humidity in %RH in Q22.10 format (22 integer
// and 10 fractional bits). Output value of 47445 represents 47445/1024 =
// 46.333%
//
// raw has 16 bits of resolution.
func (c *calibration280) compensateHumidityInt(raw, tFine int32) uint32 {
x := tFine - 76800
x1 := raw<<14 - int32(c.h4)<<20 - int32(c.h5)*x
x2 := (x1 + 16384) >> 15
x3 := (x * int32(c.h6)) >> 10
x4 := (x * int32(c.h3)) >> 11
x5 := (x3 * (x4 + 32768)) >> 10
x6 := ((x5+2097152)*int32(c.h2) + 8192) >> 14
x = x2 * x6
x = x - ((((x>>15)*(x>>15))>>7)*int32(c.h1))>>4
if x < 0 {
return 0
}
if x > 419430400 {
return 419430400 >> 12
}
return uint32(x >> 12)
}