forked from arendst/Tasmota
-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathbmp280.tc
More file actions
255 lines (226 loc) · 6.94 KB
/
Copy pathbmp280.tc
File metadata and controls
255 lines (226 loc) · 6.94 KB
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
// BMP280 Temperature & Pressure Sensor Driver
// I2C addresses: 0x76 (SDO=GND) or 0x77 (SDO=VCC)
// Chip ID: 0x58 (BMP280), vs 0x60 (BME280 with humidity)
// Scans both I2C buses, claims via Tasmota I2C system
// Reads every second, displays on web UI + JSON teleperiod
// Includes 6h chart history
#define BMP_ADDR1 0x76
#define BMP_ADDR2 0x77
#define BMP_ID 0x58
// Measurement results
float bmp_temp = 0.0;
float bmp_pres = 0.0;
int bmp_ok = 0;
int bmp_addr = 0;
int bmp_bus = 0;
char bmp_lbl[32];
// I2C data buffer
char bmp_buf[26];
// Calibration data (from registers 0x88..0x9F)
// Temperature
int dig_T1;
int dig_T2;
int dig_T3;
// Pressure
int dig_P1;
int dig_P2;
int dig_P3;
int dig_P4;
int dig_P5;
int dig_P6;
int dig_P7;
int dig_P8;
int dig_P9;
// t_fine is shared between temp and pressure compensation
int t_fine;
// Chart history (6h at 1 sample/min = 360 points)
#define CHART_LEN 360
float hist_temp[CHART_LEN];
float hist_pres[CHART_LEN];
int hist_pos;
int hist_tick;
// Sign-extend a 16-bit value to signed int
int sign16(int val) {
if (val >= 32768) {
return val - 65536;
}
return val;
}
// Read calibration data from sensor
int bmp_read_calib() {
// Read 24 bytes from 0x88..0x9F (temp + pressure calibration)
if (!i2cRead(bmp_addr, 0x88, bmp_buf, 24, bmp_bus)) return 0;
dig_T1 = bmp_buf[0] | (bmp_buf[1] << 8);
dig_T2 = sign16(bmp_buf[2] | (bmp_buf[3] << 8));
dig_T3 = sign16(bmp_buf[4] | (bmp_buf[5] << 8));
dig_P1 = bmp_buf[6] | (bmp_buf[7] << 8);
dig_P2 = sign16(bmp_buf[8] | (bmp_buf[9] << 8));
dig_P3 = sign16(bmp_buf[10] | (bmp_buf[11] << 8));
dig_P4 = sign16(bmp_buf[12] | (bmp_buf[13] << 8));
dig_P5 = sign16(bmp_buf[14] | (bmp_buf[15] << 8));
dig_P6 = sign16(bmp_buf[16] | (bmp_buf[17] << 8));
dig_P7 = sign16(bmp_buf[18] | (bmp_buf[19] << 8));
dig_P8 = sign16(bmp_buf[20] | (bmp_buf[21] << 8));
dig_P9 = sign16(bmp_buf[22] | (bmp_buf[23] << 8));
return 1;
}
// Configure sensor: oversampling x1 temp+press, normal mode, 1000ms standby
int bmp_configure() {
// config (0xF5): standby 1000ms (101), filter off (000), SPI off (0) = 0xA0
if (!i2cWrite8(bmp_addr, 0xF5, 0xA0, bmp_bus)) return 0;
// ctrl_meas (0xF4): temp os x1 (001), press os x1 (001), normal mode (11) = 0x27
if (!i2cWrite8(bmp_addr, 0xF4, 0x27, bmp_bus)) return 0;
return 1;
}
// Scan both buses and addresses
int bmp_scan() {
int bus = 0;
while (bus < 2) {
int addr = BMP_ADDR1;
while (addr <= BMP_ADDR2) {
if (i2cSetDevice(addr, bus)) {
// Check chip ID register (0xD0) — BMP280 = 0x58
int id = i2cRead8(addr, 0xD0, bus);
if (id == BMP_ID) {
bmp_addr = addr;
bmp_bus = bus;
i2cSetActiveFound(bmp_addr, "BMP280", bmp_bus);
return 1;
}
}
addr++;
}
bus++;
}
return 0;
}
// Compensate temperature — returns °C * 100 as int, sets t_fine
int bmp_comp_temp(int adc_T) {
int var1 = ((((adc_T >> 3) - (dig_T1 << 1))) * dig_T2) >> 11;
int var2 = (((((adc_T >> 4) - dig_T1) * ((adc_T >> 4) - dig_T1)) >> 12) * dig_T3) >> 14;
t_fine = var1 + var2;
int T = (t_fine * 5 + 128) >> 8;
return T;
}
// Compensate pressure — returns Pa as float
// Using float arithmetic to avoid 32-bit overflow
float bmp_comp_pres(int adc_P) {
float var1 = (float)t_fine / 2.0 - 64000.0;
float var2 = var1 * var1 * (float)dig_P6 / 32768.0;
var2 = var2 + var1 * (float)dig_P5 * 2.0;
var2 = var2 / 4.0 + (float)dig_P4 * 65536.0;
var1 = ((float)dig_P3 * var1 * var1 / 524288.0 + (float)dig_P2 * var1) / 524288.0;
var1 = (1.0 + var1 / 32768.0) * (float)dig_P1;
if (var1 == 0.0) return 0.0;
float p = 1048576.0 - (float)adc_P;
p = (p - var2 / 4096.0) * 6250.0 / var1;
var1 = (float)dig_P9 * p * p / 2147483648.0;
var2 = p * (float)dig_P8 / 32768.0;
p = p + (var1 + var2 + (float)dig_P7) / 16.0;
return p;
}
void EverySecond() {
if (!bmp_addr) {
if (!bmp_scan()) {
bmp_ok = 0;
return;
}
if (!bmp_read_calib()) {
bmp_ok = 0;
bmp_addr = 0;
return;
}
if (!bmp_configure()) {
bmp_ok = 0;
bmp_addr = 0;
return;
}
}
// Read 6 bytes from 0xF7: press[3], temp[3] (no humidity on BMP280)
if (!i2cRead(bmp_addr, 0xF7, bmp_buf, 6, bmp_bus)) {
bmp_ok = 0;
bmp_addr = 0;
return;
}
int adc_P = (bmp_buf[0] << 12) | (bmp_buf[1] << 4) | (bmp_buf[2] >> 4);
int adc_T = (bmp_buf[3] << 12) | (bmp_buf[4] << 4) | (bmp_buf[5] >> 4);
// Temperature (sets t_fine used by pressure)
int T100 = bmp_comp_temp(adc_T);
bmp_temp = (float)T100 / 100.0;
// Pressure in hPa
bmp_pres = bmp_comp_pres(adc_P) / 100.0;
bmp_ok = 1;
// Update chart history every minute
hist_tick++;
if (hist_tick >= 60) {
hist_tick = 0;
hist_temp[hist_pos % CHART_LEN] = bmp_temp;
hist_pres[hist_pos % CHART_LEN] = bmp_pres;
hist_pos++;
}
}
// Tasmota main page — charts
void WebPage() {
int n = hist_pos;
if (n > CHART_LEN) n = CHART_LEN;
if (n > 0) {
// ottelo's chart-centering compensation (margin-left:-30px wrapper)
webSend("<div style='margin-left:-30px'>");
WebChart('l', "Temperature", "\u00b0C", 0xe74c3c, hist_pos, CHART_LEN, hist_temp, 1, 0, 0, 0);
WebChart('l', "Pressure", "hPa", 0x27ae60, hist_pos, CHART_LEN, hist_pres, 1, 0, 0, 0);
webSend("</div>");
}
}
// Tasmota web UI — sensor rows
void bmp_web_label(int idx) {
char vt[32];
LGetString(idx, bmp_lbl);
sprintf(vt, "{s}BMP280 %s{m}", bmp_lbl);
webSend(vt);
}
void WebCall() {
char buf[32];
if (bmp_ok) {
bmp_web_label(0);
sprintf(buf, "%.1f °C{e}", bmp_temp);
webSend(buf);
bmp_web_label(2);
sprintf(buf, "%.1f hPa{e}", bmp_pres);
webSend(buf);
} else {
webSend("{s}BMP280{m}not found{e}");
}
}
// JSON teleperiod — appears in MQTT sensor payload
void JsonCall() {
if (!bmp_ok) return;
char buf[96];
sprintf(buf, ",\"BMP280\":{\"Temperature\":%.1f", bmp_temp);
responseAppend(buf);
sprintf(buf, ",\"Pressure\":%.1f}", bmp_pres);
responseAppend(buf);
}
void OnExit() {
if (bmp_addr) {
I2cResetActive(bmp_addr, bmp_bus);
bmp_addr = 0;
}
}
int main() {
bmp_ok = 0;
bmp_addr = 0;
hist_pos = 0;
hist_tick = 0;
if (bmp_scan()) {
addLog("BMP280 found at 0x%x on bus %d", bmp_addr, bmp_bus);
if (bmp_read_calib() && bmp_configure()) {
addLog("BMP280 calibration loaded, sensor active");
} else {
addLog("BMP280 calibration/config failed");
bmp_addr = 0;
}
} else {
addLog("BMP280 not found on any bus");
}
return 0;
}