This repository has been archived by the owner on Aug 14, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbme280_weather_station
469 lines (366 loc) · 15.3 KB
/
bme280_weather_station
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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
#define MY_RADIO_NRF24
#include <MySensors.h>
#include <Wire.h>
#include "BlueDot_BME280.h"
#include <math.h>
BlueDot_BME280 bme1; //Object for Sensor 1
int bme1Detected = 0;
#define MY_REPEATER_FEATURE
#define CHILD_ID_HUM 0
#define CHILD_ID_TEMP 1
#define CHILD_ID_BARO 2
#define CHILD_ID_FORECAST 3
#define CHILD_ID_DEW 4
const float ALTITUDE = 173;
const unsigned long SLEEP_TIME = 60000;
const char *weather[] = { "stable", "sunny", "cloudy", "unstable", "thunderstorm", "unknown" };
enum FORECAST
{
STABLE = 0, // "Stable Weather Pattern"
SUNNY = 1, // "Slowly rising Good Weather", "Clear/Sunny "
CLOUDY = 2, // "Slowly falling L-Pressure ", "Cloudy/Rain "
UNSTABLE = 3, // "Quickly rising H-Press", "Not Stable"
THUNDERSTORM = 4, // "Quickly falling L-Press", "Thunderstorm"
UNKNOWN = 5 // "Unknown (More Time needed)
};
float lastPressure = -1;
float lastTemp = -1;
float lastHum = -1;
int lastForecast = -1;
float lastDew = -1;
const int LAST_SAMPLES_COUNT = 5;
float lastPressureSamples[LAST_SAMPLES_COUNT];
#define CONVERSION_FACTOR (1.0/10.0)
int minuteCount = 0;
bool firstRound = true;
float pressureAvg;
float pressureAvg2;
float dP_dt;
boolean metric;
MyMessage msgHum(CHILD_ID_HUM, V_HUM);
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
MyMessage msgBaro(CHILD_ID_BARO, V_PRESSURE);
MyMessage msgForecast(CHILD_ID_FORECAST, V_FORECAST);
MyMessage msgDew(CHILD_ID_DEW, V_TEMP);
float getLastPressureSamplesAverage()
{
float lastPressureSamplesAverage = 0;
for (int i = 0; i < LAST_SAMPLES_COUNT; i++)
{
lastPressureSamplesAverage += lastPressureSamples[i];
}
lastPressureSamplesAverage /= LAST_SAMPLES_COUNT;
return lastPressureSamplesAverage;
}
// Algorithm found here
// http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf
// Pressure in hPa --> forecast done by calculating kPa/h
int sample(float pressure)
{
// Calculate the average of the last n minutes.
int index = minuteCount % LAST_SAMPLES_COUNT;
lastPressureSamples[index] = pressure;
minuteCount++;
if (minuteCount > 185)
{
minuteCount = 6;
}
if (minuteCount == 5)
{
pressureAvg = getLastPressureSamplesAverage();
}
else if (minuteCount == 35)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change * 2; // note this is for t = 0.5hour
}
else
{
dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value.
}
}
else if (minuteCount == 65)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) //first time initial 3 hour
{
dP_dt = change; //note this is for t = 1 hour
}
else
{
dP_dt = change / 2; //divide by 2 as this is the difference in time from 0 value
}
}
else if (minuteCount == 95)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 1.5; // note this is for t = 1.5 hour
}
else
{
dP_dt = change / 2.5; // divide by 2.5 as this is the difference in time from 0 value
}
}
else if (minuteCount == 125)
{
float lastPressureAvg = getLastPressureSamplesAverage();
pressureAvg2 = lastPressureAvg; // store for later use.
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 2; // note this is for t = 2 hour
}
else
{
dP_dt = change / 3; // divide by 3 as this is the difference in time from 0 value
}
}
else if (minuteCount == 155)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 2.5; // note this is for t = 2.5 hour
}
else
{
dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value
}
}
else if (minuteCount == 185)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 3; // note this is for t = 3 hour
}
else
{
dP_dt = change / 4; // divide by 4 as this is the difference in time from 0 value
}
pressureAvg = pressureAvg2; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past.
firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop.
}
int forecast = UNKNOWN;
if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval.
{
forecast = UNKNOWN;
}
else if (dP_dt < (-0.25))
{
forecast = THUNDERSTORM;
}
else if (dP_dt > 0.25)
{
forecast = UNSTABLE;
}
else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05)))
{
forecast = CLOUDY;
}
else if ((dP_dt > 0.05) && (dP_dt < 0.25))
{
forecast = SUNNY;
}
else if ((dP_dt >(-0.05)) && (dP_dt < 0.05))
{
forecast = STABLE;
}
else
{
forecast = UNKNOWN;
}
// uncomment when debugging
//Serial.print(F("Forecast at minute "));
//Serial.print(minuteCount);
//Serial.print(F(" dP/dt = "));
//Serial.print(dP_dt);
//Serial.print(F("kPa/h --> "));
//Serial.println(weather[forecast]);
return forecast;
}
void presentation()
{
sendSketchInfo("bme280 weather station", "stable", 1);
present(CHILD_ID_HUM, S_HUM, "humidity");
present(CHILD_ID_TEMP, S_TEMP, "tempreture");
present(CHILD_ID_BARO, S_BARO, "pressure");
present(CHILD_ID_FORECAST, S_BARO, "weather forcast");
present(CHILD_ID_DEW, S_TEMP, "Dew point");
}
void setup() {
bme1.parameter.communication = 0; //Setting communication for Sensor 1 (bme1)
//*********************************************************************
//Set the I2C address of your breakout board
bme1.parameter.I2CAddress = 0x77; //I2C Address for Sensor 1 (bme1)
//*********************************************************************
//Now choose on which mode your device will run
//On doubt, just leave on normal mode, that's the default value
//bme1.parameter.sensorMode = 0b00; //In sleep mode no measurements are performed, but power consumption is at a minimum
//bme1.parameter.sensorMode = 0b01; //In forced mode a single measured is performed and the device returns automatically to sleep mode
bme1.parameter.sensorMode = 0b11; //In normal mode the sensor measures continually (default value)
//*********************************************************************
//Great! Now set up the internal IIR Filter
//The IIR (Infinite Impulse Response) filter suppresses high frequency fluctuations
//In short, a high factor value means less noise, but measurements are also less responsive
//You can play with these values and check the results!
//In doubt just leave on default
//bme1.parameter.IIRfilter = 0b000; //factor 0 (filter off)
//bme1.parameter.IIRfilter = 0b001; //factor 2
//bme1.parameter.IIRfilter = 0b010; //factor 4
//bme1.parameter.IIRfilter = 0b011; //factor 8
bme1.parameter.IIRfilter = 0b101; //factor 16 (default value)
//*********************************************************************
//Next you'll define the oversampling factor for the humidity measurements
//Again, higher values mean less noise, but slower responses
//If you don't want to measure humidity, set the oversampling to zero
//bme1.parameter.humidOversampling = 0b000; //factor 0 (Disable humidity measurement)
//bme1.parameter.humidOversampling = 0b001; //factor 1
//bme1.parameter.humidOversampling = 0b010; //factor 2
//bme1.parameter.humidOversampling = 0b011; //factor 4
//bme1.parameter.humidOversampling = 0b100; //factor 8
bme1.parameter.humidOversampling = 0b101; //factor 16
//*********************************************************************
//Now define the oversampling factor for the temperature measurements
//You know now, higher values lead to less noise but slower measurements
//bme1.parameter.tempOversampling = 0b000; //factor 0 (Disable temperature measurement)
//bme1.parameter.tempOversampling = 0b001; //factor 1
//bme1.parameter.tempOversampling = 0b010; //factor 2
//bme1.parameter.tempOversampling = 0b011; //factor 4
//bme1.parameter.tempOversampling = 0b100; //factor 8
bme1.parameter.tempOversampling = 0b101; //factor 16
//*********************************************************************
//Finally, define the oversampling factor for the pressure measurements
//For altitude measurements a higher factor provides more stable values
//On doubt, just leave it on default
//bme1.parameter.pressOversampling = 0b000; //factor 0 (Disable pressure measurement)
//bme1.parameter.pressOversampling = 0b001; //factor 1
//bme1.parameter.pressOversampling = 0b010; //factor 2
//bme1.parameter.pressOversampling = 0b011; //factor 4
//bme1.parameter.pressOversampling = 0b100; //factor 8
bme1.parameter.pressOversampling = 0b101; //factor 16
//*********************************************************************
//For precise altitude measurements please put in the current pressure corrected for the sea level
//On doubt, just leave the standard pressure as default (1013.25 hPa);
bme1.parameter.pressureSeaLevel = 1013.25; //default value of 1013.25 hPa (Sensor 1)
//Also put in the current average temperature outside (yes, really outside!)
//For slightly less precise altitude measurements, just leave the standard temperature as default (15°C);
bme1.parameter.tempOutsideCelsius = 15; //default value of 15°C
//bme1.parameter.tempOutsideFahrenheit = 77; //default value of 59°F
//*********************************************************************
if (bme1.init() != 0x60)
{
Serial.println(F("Ops! First BME280 Sensor not found!"));
Serial.println(F("Please check your connections."));
bme1Detected = 0;
}
else
{
Serial.println(F("First BME280 Sensor detected!"));
bme1Detected = 1;
}
Serial.println();
Serial.println();
}
//*********************************************************************
void loop() {
Serial.print(F("Duration in Seconds:\t\t\t\t"));
Serial.println(float(millis())/1000);
if (bme1Detected)
{
Serial.print(F("Temperature in Celsius from Sensor 1:\t\t"));
Serial.println(bme1.readTempC());
Serial.print(F("Temperature in Fahrenheit from Sensor 1:\t"));
Serial.println(bme1.readTempF());
Serial.print(F("Humidity in % from Sensor 1:\t\t\t"));
Serial.println(bme1.readHumidity());
Serial.print(F("Pressure in hPa from Sensor 1:\t\t\t"));
Serial.println(bme1.readPressure());
Serial.print(F("Altitude in Meters from Sensor 1:\t\t"));
Serial.println(bme1.readAltitudeMeter());
Serial.print(F("Altitude in Feet from Sensor 1:\t\t\t"));
Serial.println(bme1.readAltitudeFeet());
}
else
{
Serial.print(F("Temperature in Celsius from Sensor 1:\t\t"));
Serial.println(F("Null"));
Serial.print(F("Temperature in Fahrenheit from Sensor 1:\t"));
Serial.println(F("Null"));
Serial.print(F("Humidity in % from Sensor 1:\t\t\t"));
Serial.println(F("Null"));
Serial.print(F("Pressure in hPa from Sensor 1:\t\t\t"));
Serial.println(F("Null"));
Serial.print(F("Altitude in Meters from Sensor 1:\t\t"));
Serial.println(F("Null"));
Serial.print(F("Altitude in Feet from Sensor 1:\t\t\t"));
Serial.println(F("Null"));
}
Serial.println();
Serial.println();
float temperature = bme1.readTempC(); // must get temp first
float humidity = bme1.readHumidity();
float pressure_local = bme1.readPressure(); // Get pressure at current location
float pressure = pressure_local/pow((1.0 - ( ALTITUDE / 44330.0 )), 5.255); // Adjust to sea level pressure using user altitude
int forecast = sample(pressure);
const float TempCoefficient = -0.15; //The temperature compensation coefficient value to guaratnee RH accuracy between 20-80%RH
const float DewConstA = 8.1332; //Constants required to calclulate the partial pressure and dew point. See datasheet page 16
const float DewConstB = 1762.39;
const float DewConstC = 235.66;
float HumidityCompFL; //%RH value that has been temperature compensated to gurantee performance of +-3% between 20-80%RH
float ParitalPressureFL; //Calculated partial pressure in mmHg. used to calculate Dew Point.
float DewPointFL; //calculated Dew Point in degrees Celcius
float Dew;
//The relative humidty value read from directly from the chip is not the optimised value.
//We must perfrom the 'temperature Coefficient Compensation Equation" specified on page 4 of the datasheet
//RHcompensated = RHactual + (25 - TEMPactual) * coefficient
//The coefficient value is -0.15%RH/C - from page 3 of datasheet
HumidityCompFL = humidity +(25 - temperature) * TempCoefficient;
//To calculate the dew point, the partial pressure must be determined first. See datasheet page 16 for details.
//ParitalPress = 10 ^ (A - (B / (Temp + C)))
ParitalPressureFL = (DewConstA - (DewConstB / (temperature + DewConstC)));
ParitalPressureFL = pow(10, ParitalPressureFL);
//Dew point is calculated using the partial pressure, humidity and temperature.
//The datasheet on page 16 doesn't say to use the temperature compensated
//RH value and is ambiguous. It says "Ambient humidity in %RH, computed from HTU21D(F) sensor".
//However, as Bjoern Kasper pointed out, when considering a Moliere h-x-diagram, temperature compensated RH% should be used.
//DewPoint = -(C + B/(log(RH * PartialPress /100) - A ))
//Arduino doesn't have a LOG base 10 function. But Arduino can use the AVRLibC libraries, so we'll use the "math.h".
DewPointFL = (HumidityCompFL * ParitalPressureFL / 100); //do the innermost brackets
DewPointFL = log10(DewPointFL) - DewConstA; //we have calculated the denominator of the equation
DewPointFL = DewConstB / DewPointFL; //the whole fraction of the equation has been calculated
DewPointFL = -(DewPointFL + DewConstC); //The whole dew point calculation has been performed
Dew = DewPointFL;
if (temperature != lastTemp)
{
send(msgTemp.set(temperature, 1));
lastTemp = temperature;
}
if (Dew != lastDew)
{
send(msgDew.set(Dew, 1));
lastDew = Dew;
}
if (humidity != lastHum)
{
send(msgHum.set(humidity, 1));
lastHum = humidity;
}
if (pressure != lastPressure)
{
send(msgBaro.set(pressure, 2));
lastPressure = pressure;
}
send(msgForecast.set(weather[forecast]));
lastForecast = forecast;
wait(10000);
}