Skip to content

Commit d43d2b3

Browse files
vskyvsky279
vsky
authored andcommitted
bme280 driver in Lua+C
1 parent ebfce4a commit d43d2b3

File tree

8 files changed

+905
-0
lines changed

8 files changed

+905
-0
lines changed

app/include/user_modules.h

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//#define LUA_USE_MODULES_BLOOM
1717
//#define LUA_USE_MODULES_BMP085
1818
//#define LUA_USE_MODULES_BME280
19+
//#define LUA_USE_MODULES_BME280_MATH
1920
//#define LUA_USE_MODULES_BME680
2021
//#define LUA_USE_MODULES_COAP
2122
//#define LUA_USE_MODULES_COLOR_UTILS

app/modules/bme280.c

+2
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ static int bme280_lua_setup(lua_State* L) {
236236
uint8_t const bit3 = 0b111;
237237
uint8_t const bit2 = 0b11;
238238

239+
platform_print_deprecation_note("bme280", "soon. Use bme280math and bme280 Lua module instead");
240+
239241
bme280_mode = (!lua_isnumber(L, 4)?BME280_NORMAL_MODE:(luaL_checkinteger(L, 4)&bit2)) // 4-th parameter: power mode
240242
| ((!lua_isnumber(L, 2)?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 2)&bit3)) << 2) // 2-nd parameter: pressure oversampling
241243
| ((!lua_isnumber(L, 1)?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 1)&bit3)) << 5); // 1-st parameter: temperature oversampling

app/modules/bme280_math.c

+357
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
// ***************************************************************************
2+
// BMP280 module for ESP8266 with nodeMCU
3+
//
4+
// Written by Lukas Voborsky, @voborsky
5+
//
6+
// MIT license, http://opensource.org/licenses/MIT
7+
// ***************************************************************************
8+
9+
// #define NODE_DEBUG
10+
11+
#include "module.h"
12+
#include "lauxlib.h"
13+
#include "platform.h"
14+
#include "user_interface.h"
15+
#include <math.h>
16+
17+
/****************************************************/
18+
/**\name registers definition */
19+
/***************************************************/
20+
#define BME280_REGISTER_CONTROL (0xF4)
21+
#define BME280_REGISTER_CONTROL_HUM (0xF2)
22+
#define BME280_REGISTER_CONFIG (0xF5)
23+
#define BME280_REGISTER_CHIPID (0xD0)
24+
#define BME280_REGISTER_VERSION (0xD1)
25+
#define BME280_REGISTER_SOFTRESET (0xE0)
26+
#define BME280_REGISTER_CAL26 (0xE1)
27+
#define BME280_REGISTER_PRESS (0xF7) // 0xF7-0xF9
28+
#define BME280_REGISTER_TEMP (0xFA) // 0xFA-0xFC
29+
#define BME280_REGISTER_HUM (0xFD) // 0xFD-0xFE
30+
31+
#define BME280_REGISTER_DIG_T (0x88) // 0x88-0x8D ( 6)
32+
#define BME280_REGISTER_DIG_P (0x8E) // 0x8E-0x9F (18)
33+
#define BME280_REGISTER_DIG_H1 (0xA1) // 0xA1 ( 1)
34+
#define BME280_REGISTER_DIG_H2 (0xE1) // 0xE1-0xE7 ( 7)
35+
/****************************************************/
36+
/**\name I2C ADDRESS DEFINITIONS */
37+
/***************************************************/
38+
#define BME280_I2C_ADDRESS1 (0x76)
39+
#define BME280_I2C_ADDRESS2 (0x77)
40+
/****************************************************/
41+
/**\name POWER MODE DEFINITIONS */
42+
/***************************************************/
43+
/* Sensor Specific constants */
44+
#define BME280_SLEEP_MODE (0x00)
45+
#define BME280_FORCED_MODE (0x01)
46+
#define BME280_NORMAL_MODE (0x03)
47+
#define BME280_SOFT_RESET_CODE (0xB6)
48+
/****************************************************/
49+
/**\name OVER SAMPLING DEFINITIONS */
50+
/***************************************************/
51+
#define BME280_OVERSAMP_1X (0x01)
52+
#define BME280_OVERSAMP_2X (0x02)
53+
#define BME280_OVERSAMP_4X (0x03)
54+
#define BME280_OVERSAMP_8X (0x04)
55+
#define BME280_OVERSAMP_16X (0x05)
56+
/****************************************************/
57+
/**\name STANDBY TIME DEFINITIONS */
58+
/***************************************************/
59+
#define BME280_STANDBY_TIME_1_MS (0x00)
60+
#define BME280_STANDBY_TIME_63_MS (0x01)
61+
#define BME280_STANDBY_TIME_125_MS (0x02)
62+
#define BME280_STANDBY_TIME_250_MS (0x03)
63+
#define BME280_STANDBY_TIME_500_MS (0x04)
64+
#define BME280_STANDBY_TIME_1000_MS (0x05)
65+
#define BME280_STANDBY_TIME_10_MS (0x06)
66+
#define BME280_STANDBY_TIME_20_MS (0x07)
67+
/****************************************************/
68+
/**\name FILTER DEFINITIONS */
69+
/***************************************************/
70+
#define BME280_FILTER_COEFF_OFF (0x00)
71+
#define BME280_FILTER_COEFF_2 (0x01)
72+
#define BME280_FILTER_COEFF_4 (0x02)
73+
#define BME280_FILTER_COEFF_8 (0x03)
74+
#define BME280_FILTER_COEFF_16 (0x04)
75+
/****************************************************/
76+
/**\data type definition */
77+
/***************************************************/
78+
#define BME280_S32_t int32_t
79+
#define BME280_U32_t uint32_t
80+
#define BME280_S64_t int64_t
81+
82+
#define BME280_SAMPLING_DELAY 113 //maximum measurement time in ms for maximum oversampling for all measures = 1.25 + 2.3*16 + 2.3*16 + 0.575 + 2.3*16 + 0.575 ms
83+
84+
// #define r16s(reg) ((int16_t)r16u(reg))
85+
// #define r16sLE(reg) ((int16_t)r16uLE(reg))
86+
87+
// #define bme280_adc_P(void) r24u(BME280_REGISTER_PRESS)
88+
// #define bme280_adc_T(void) r24u(BME280_REGISTER_TEMP)
89+
// #define bme280_adc_H(void) r16u(BME280_REGISTER_HUM)
90+
91+
typedef struct {
92+
uint16_t dig_T1;
93+
int16_t dig_T2;
94+
int16_t dig_T3;
95+
uint16_t dig_P1;
96+
int16_t dig_P2;
97+
int16_t dig_P3;
98+
int16_t dig_P4;
99+
int16_t dig_P5;
100+
int16_t dig_P6;
101+
int16_t dig_P7;
102+
int16_t dig_P8;
103+
int16_t dig_P9;
104+
uint8_t dig_H1;
105+
int16_t dig_H2;
106+
uint8_t dig_H3;
107+
int16_t dig_H4;
108+
int16_t dig_H5;
109+
int8_t dig_H6;
110+
} bme280_data_t;
111+
typedef bme280_data_t* bme280_data_p;
112+
bme280_data_p bme280_data;
113+
114+
115+
BME280_S32_t bme280_t_fine;
116+
117+
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
118+
// t_fine carries fine temperature as global value
119+
BME280_S32_t bme280_compensate_T(BME280_S32_t adc_T) {
120+
BME280_S32_t var1, var2, T;
121+
var1 = ((((adc_T>>3) - ((BME280_S32_t)(*bme280_data).dig_T1<<1))) * ((BME280_S32_t)(*bme280_data).dig_T2)) >> 11;
122+
var2 = (((((adc_T>>4) - ((BME280_S32_t)(*bme280_data).dig_T1)) * ((adc_T>>4) - ((BME280_S32_t)(*bme280_data).dig_T1))) >> 12) *
123+
((BME280_S32_t)(*bme280_data).dig_T3)) >> 14;
124+
bme280_t_fine = var1 + var2;
125+
T = (bme280_t_fine * 5 + 128) >> 8;
126+
return T;
127+
}
128+
129+
// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
130+
// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
131+
BME280_U32_t bme280_compensate_P(BME280_S32_t adc_P) {
132+
BME280_S64_t var1, var2, p;
133+
var1 = ((BME280_S64_t)bme280_t_fine) - 128000;
134+
var2 = var1 * var1 * (BME280_S64_t)(*bme280_data).dig_P6;
135+
var2 = var2 + ((var1*(BME280_S64_t)(*bme280_data).dig_P5)<<17);
136+
var2 = var2 + (((BME280_S64_t)(*bme280_data).dig_P4)<<35);
137+
var1 = ((var1 * var1 * (BME280_S64_t)(*bme280_data).dig_P3)>>8) + ((var1 * (BME280_S64_t)(*bme280_data).dig_P2)<<12);
138+
var1 = (((((BME280_S64_t)1)<<47)+var1))*((BME280_S64_t)(*bme280_data).dig_P1)>>33;
139+
if (var1 == 0) {
140+
return 0; // avoid exception caused by division by zero
141+
}
142+
p = 1048576-adc_P;
143+
p = (((p<<31)-var2)*3125)/var1;
144+
var1 = (((BME280_S64_t)(*bme280_data).dig_P9) * (p>>13) * (p>>13)) >> 25;
145+
var2 = (((BME280_S64_t)(*bme280_data).dig_P8) * p) >> 19;
146+
p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)(*bme280_data).dig_P7)<<4);
147+
p = (p * 10) >> 8;
148+
return (BME280_U32_t)p;
149+
}
150+
151+
// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
152+
// Output value of “47445” represents 47445/1024 = 46.333 %RH
153+
BME280_U32_t bme280_compensate_H(BME280_S32_t adc_H) {
154+
BME280_S32_t v_x1_u32r;
155+
156+
v_x1_u32r = (bme280_t_fine - ((BME280_S32_t)76800));
157+
v_x1_u32r = (((((adc_H << 14) - (((BME280_S32_t)(*bme280_data).dig_H4) << 20) - (((BME280_S32_t)(*bme280_data).dig_H5) * v_x1_u32r)) +
158+
((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)(*bme280_data).dig_H6)) >> 10) * (((v_x1_u32r *
159+
((BME280_S32_t)(*bme280_data).dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) *
160+
((BME280_S32_t)(*bme280_data).dig_H2) + 8192) >> 14));
161+
v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)(*bme280_data).dig_H1)) >> 4));
162+
v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
163+
v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
164+
v_x1_u32r = v_x1_u32r>>12;
165+
return (BME280_U32_t)((v_x1_u32r * 1000)>>10);
166+
}
167+
168+
double ln(double x) {
169+
double y = (x-1)/(x+1);
170+
double y2 = y*y;
171+
double r = 0;
172+
for (int8_t i=33; i>0; i-=2) { //we've got the power
173+
r = 1.0/(double)i + y2 * r;
174+
}
175+
return 2*y*r;
176+
}
177+
178+
uint32_t bme280_h = 0; // buffer last qfe2qnh calculation
179+
double bme280_hc = 1.0;
180+
181+
double bme280_qfe2qnh(double qfe, double h) {
182+
double hc;
183+
if (bme280_h == h) {
184+
hc = bme280_hc;
185+
} else {
186+
hc = pow((double)(1.0 - 2.25577e-5 * h), (double)(-5.25588));
187+
bme280_hc = hc; bme280_h = h;
188+
}
189+
double qnh = (double)qfe * hc;
190+
return qnh;
191+
}
192+
193+
int bme280_lua_setup(lua_State* L) {
194+
uint8_t bme280_mode = 0; // stores oversampling settings
195+
uint8_t bme280_ossh = 0; // stores humidity oversampling settings
196+
uint8_t config;
197+
198+
uint8_t const bit3 = 0b111;
199+
uint8_t const bit2 = 0b11;
200+
201+
bme280_mode = (!lua_isnumber(L, 5)?BME280_NORMAL_MODE:(luaL_checkinteger(L, 5)&bit2)) // 4-th parameter: power mode
202+
| ((!lua_isnumber(L, 3)?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 3)&bit3)) << 2) // 2-nd parameter: pressure oversampling
203+
| ((!lua_isnumber(L, 2)?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 2)&bit3)) << 5); // 1-st parameter: temperature oversampling
204+
205+
bme280_ossh = (!lua_isnumber(L, 4))?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 4)&bit3); // 3-rd parameter: humidity oversampling
206+
207+
config = ((!lua_isnumber(L, 6)?BME280_STANDBY_TIME_20_MS:(luaL_checkinteger(L, 6)&bit3))<< 5) // 5-th parameter: inactive duration in normal mode
208+
| ((!lua_isnumber(L, 7)?BME280_FILTER_COEFF_16:(luaL_checkinteger(L, 7)&bit3)) << 2); // 6-th parameter: IIR filter
209+
// NODE_DBG("mode: %x\nhumidity oss: %x\nconfig: %x\n", bme280_mode, bme280_ossh, config);
210+
211+
#define r16uLE_buf(reg) (uint16_t)((reg[1] << 8) | reg[0])
212+
#define r16sLE_buf(reg) (int16_t)(r16uLE_buf(reg))
213+
size_t reg_len;
214+
const char *buf = luaL_checklstring(L, 1, &reg_len);
215+
216+
bme280_data = (bme280_data_p) memset(lua_newuserdata(L, sizeof(*bme280_data)), 0, sizeof(*bme280_data)); // first parameter to be returned
217+
const uint8_t *reg;
218+
219+
reg = buf;
220+
(*bme280_data).dig_T1 = r16uLE_buf(reg); reg+=2;
221+
(*bme280_data).dig_T2 = r16sLE_buf(reg); reg+=2;
222+
(*bme280_data).dig_T3 = r16sLE_buf(reg); reg+=2;
223+
// NODE_DBG("dig_T: %d\t%d\t%d\n", (*bme280_data).dig_T1, (*bme280_data).dig_T2, (*bme280_data).dig_T3);
224+
225+
(*bme280_data).dig_P1 = r16uLE_buf(reg); reg+=2;
226+
(*bme280_data).dig_P2 = r16sLE_buf(reg); reg+=2;
227+
(*bme280_data).dig_P3 = r16sLE_buf(reg); reg+=2;
228+
(*bme280_data).dig_P4 = r16sLE_buf(reg); reg+=2;
229+
(*bme280_data).dig_P5 = r16sLE_buf(reg); reg+=2;
230+
(*bme280_data).dig_P6 = r16sLE_buf(reg); reg+=2;
231+
(*bme280_data).dig_P7 = r16sLE_buf(reg); reg+=2;
232+
(*bme280_data).dig_P8 = r16sLE_buf(reg); reg+=2;
233+
(*bme280_data).dig_P9 = r16sLE_buf(reg); reg+=2;
234+
// NODE_DBG("dig_P: %d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", (*bme280_data).dig_P1, (*bme280_data).dig_P2,(*bme280_data).dig_P3, (*bme280_data).dig_P4, (*bme280_data).dig_P5, (*bme280_data).dig_P6, (*bme280_data).dig_P7,(*bme280_data).dig_P8, (*bme280_data).dig_P9);
235+
236+
if (reg_len>=6+18) { // is BME?
237+
(*bme280_data).dig_H1 = (uint8)reg[0]; reg+=1;
238+
(*bme280_data).dig_H2 = r16sLE_buf(reg); reg+=2;
239+
(*bme280_data).dig_H3 = reg[0]; reg++;
240+
(*bme280_data).dig_H4 = (int16_t)reg[0] << 4 | (reg[1] & 0x0F); reg+=1; // H4[11:4 3:0] = 0xE4[7:0] 0xE5[3:0] 12-bit signed
241+
(*bme280_data).dig_H5 = (int16_t)reg[1] << 4 | (reg[0] >> 4); reg+=2; // H5[11:4 3:0] = 0xE6[7:0] 0xE5[7:4] 12-bit signed
242+
(*bme280_data).dig_H6 = (int8_t)reg[0];
243+
NODE_DBG("dig_H: %d\t%d\t%d\t%d\t%d\t%d\n", (*bme280_data).dig_H1, (*bme280_data).dig_H2, (*bme280_data).dig_H3, (*bme280_data).dig_H4, (*bme280_data).dig_H5, (*bme280_data).dig_H6);
244+
}
245+
#undef r16uLE_buf
246+
#undef r16sLE_buf
247+
248+
int i = 1;
249+
char cfg[2]={'\0', '\0'};
250+
lua_createtable(L, 3, 0); /* configuration table */
251+
cfg[0]=(char)config;
252+
lua_pushstring(L, cfg);
253+
lua_rawseti(L, -2, i++);
254+
cfg[0]=(char)bme280_ossh;
255+
lua_pushstring(L, cfg);
256+
lua_rawseti(L, -2, i++);
257+
cfg[0]=(char)bme280_mode;
258+
lua_pushstring(L, cfg);
259+
lua_rawseti(L, -2, i);
260+
return 2;
261+
}
262+
263+
// Return T, QFE, H if no altitude given
264+
// Return T, QFE, H, QNH if altitude given
265+
int bme280_lua_read(lua_State* L) {
266+
double qfe;
267+
268+
bme280_data = (bme280_data_p)lua_touserdata(L, 1);
269+
270+
size_t reg_len;
271+
const char *buf = luaL_checklstring(L, 2, &reg_len); // registers are P[3], T[3], H[2]
272+
273+
if (reg_len != 8 && reg_len !=6) {
274+
luaL_error(L, "invalid readout data");
275+
}
276+
277+
uint8_t calc_qnh = lua_isnumber(L, 3);
278+
279+
// Must do Temp first since bme280_t_fine is used by the other compensation functions
280+
uint32_t adc_T = (uint32_t)(((buf[3] << 16) | (buf[4] << 8) | buf[5]) >> 4);
281+
if (adc_T == 0x80000 || adc_T == 0xfffff)
282+
return 0;
283+
lua_pushnumber(L, bme280_compensate_T(adc_T)/100.0);
284+
285+
uint32_t adc_P = (uint32_t)(((buf[0] << 16) | (buf[1] << 8) | buf[2]) >> 4);
286+
NODE_DBG("adc_P: %d\n", adc_P);
287+
if (adc_P ==0x80000 || adc_P == 0xfffff) {
288+
lua_pushnil(L);
289+
calc_qnh = 0;
290+
} else {
291+
qfe = bme280_compensate_P(adc_P)/1000.0;
292+
lua_pushnumber (L, qfe);
293+
}
294+
295+
uint32_t adc_H = (uint32_t)((buf[6] << 8) | buf[7]);
296+
if (reg_len!=8 || adc_H == 0x8000 || adc_H == 0xffff)
297+
lua_pushnil(L);
298+
else
299+
lua_pushnumber (L, bme280_compensate_H(adc_H)/1000.0);
300+
301+
if (calc_qnh) { // have altitude
302+
int32_t h = luaL_checknumber(L, 3);
303+
double qnh = bme280_qfe2qnh(qfe, h);
304+
lua_pushnumber (L, qnh);
305+
return 4;
306+
}
307+
return 3;
308+
}
309+
310+
int bme280_lua_qfe2qnh(lua_State* L) {
311+
if (lua_isuserdata(L, 1) || lua_istable(L, 1)) { // allow to call it as object method, userdata have no use here
312+
lua_remove(L, 1);
313+
}
314+
double qfe = luaL_checknumber(L, 1);
315+
double h = luaL_checknumber(L, 2);
316+
double qnh = bme280_qfe2qnh(qfe, h);
317+
lua_pushnumber(L, qnh);
318+
return 1;
319+
}
320+
321+
int bme280_lua_altitude(lua_State* L) {
322+
if (lua_isuserdata(L, 1) || lua_istable(L, 1)) { // allow to call it as object method, userdata have no use here
323+
lua_remove(L, 1);
324+
}
325+
double P = luaL_checknumber(L, 1);
326+
double qnh = luaL_checknumber(L, 2);
327+
double h = (1.0 - pow((double)P/(double)qnh, 1.0/5.25588)) / 2.25577e-5;
328+
lua_pushnumber (L, h);
329+
return 1;
330+
}
331+
332+
int bme280_lua_dewpoint(lua_State* L) {
333+
if (lua_isuserdata(L, 1) || lua_istable(L, 1)) { // allow to call it as object method, userdata have no use here
334+
lua_remove(L, 1);
335+
}
336+
double H = luaL_checknumber(L, 1)/100.0; // percent
337+
double T = luaL_checknumber(L, 2);
338+
339+
const double c243 = 243.5;
340+
const double c17 = 17.67;
341+
double c = ln(H) + ((c17 * T) / (c243 + T));
342+
double d = (c243 * c)/(c17 - c);
343+
344+
lua_pushnumber (L, d);
345+
return 1;
346+
}
347+
348+
LROT_BEGIN(bme280_math, NULL, 0)
349+
LROT_FUNCENTRY( setup, bme280_lua_setup )
350+
LROT_FUNCENTRY( read, bme280_lua_read )
351+
LROT_FUNCENTRY( qfe2qnh, bme280_lua_qfe2qnh )
352+
LROT_FUNCENTRY( altitude, bme280_lua_altitude )
353+
LROT_FUNCENTRY( dewpoint, bme280_lua_dewpoint )
354+
LROT_END(bme280_math, NULL, 0)
355+
356+
357+
NODEMCU_MODULE(BME280_MATH, "bme280_math", bme280_math, NULL);

0 commit comments

Comments
 (0)