/
MyS097.ino
326 lines (291 loc) · 8.9 KB
/
MyS097.ino
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
/*
* Changelog: Kommentare zum weiteren Vorgehen eingefügt
* BME280
* Standort: Raum hinter Garage
*/
#define SN "BME280"
#define SV "0.4"
//#define MY_DEBUG
//#define MY_DEBUG_LOCAL
// Enable RS485 transport layer
#define MY_RS485
#define MY_RS485_HWSERIAL Serial
#define MY_SPLASH_SCREEN_DISABLED
// Define this to enables DE-pin management on defined pin
//#define MY_RS485_DE_PIN 2
// Set RS485 baud rate to use
#define MY_RS485_BAUD_RATE 19200 //57600 //38400 //9600
//#define MY_RS485_SOH_COUNT 3
#define MY_NODE_ID 97
#define MY_TRANSPORT_WAIT_READY_MS 20000
#include <Arduino.h>
//#include <SPI.h>
#define MY_BME_ENABLED
#ifdef MY_BME_ENABLED
#include <BME280I2C.h> // From Library Manager, comes with the BME280-lib by Tyler Glenn
#define MY_FORECAST
#endif
#include <Wire.h>
#include <MySensors.h>
//für die millis()-Berechnung, wann wieder gesendet werden soll
unsigned long SEND_FREQUENCY = 180000; // Sleep time between reads (in milliseconds)
unsigned long lastSend = 0;
#define BARO_CHILD 1
#define TEMP_CHILD 2
#define HUM_CHILD 3
BME280I2C bme;
unsigned long lastSendBme = 0;
//#define SEALEVELPRESSURE_HPA (1013.25)
float lastPressure = -1;
float lastTemp = -1;
float lastHum = -1;
float temperature(NAN), humidity(NAN), pressureBme(NAN);
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
//uint8_t pressureUnit (1);
// unit: B000 = Pa, B001 = hPa, B010 = Hg, B011 = atm, B100 = bar, B101 = torr, B110 = N/m^2, B111 = psi
MyMessage tempMsg(TEMP_CHILD, V_TEMP);
MyMessage pressureMsg(BARO_CHILD, V_PRESSURE);
MyMessage humMsg(HUM_CHILD, V_HUM);
//bme: Value according to MySensors for forecast accuracy; do not change
unsigned long bmeDelayTime = 60000;
const char *weather[] = { "stable", "sunny", "cloudy", "unstable", "thunderstorm", "unknown" };
uint8_t lastForecast = -1;
const uint8_t LAST_SAMPLES_COUNT = 5;
float lastPressureSamples[LAST_SAMPLES_COUNT];
// this CONVERSION_FACTOR is used to convert from Pa to kPa in forecast algorithm
// get kPa/h be dividing hPa by 10
#define CONVERSION_FACTOR (1.0/10.0)
int minuteCount = 0;
bool firstRound = true;
// average value is used in forecast algorithm.
float pressureAvg;
// average after 2 hours is used as reference value for the next iteration.
float pressureAvg2;
float dP_dt;
MyMessage forecastMsg(BARO_CHILD, V_FORECAST);
bool metric = true;
void before()
{
Wire.begin();
bme.begin();
}
void presentation() {
sendSketchInfo(SN, SV);
present(BARO_CHILD, S_BARO);
present(TEMP_CHILD, S_TEMP);
present(HUM_CHILD, S_HUM);
}
void setup() {
metric = getControllerConfig().isMetric;
}
void loop()
{
unsigned long currentTime = millis();
// Only send values at a maximum frequency
if (currentTime - lastSend > bmeDelayTime) {
bme.read(pressureBme, temperature, humidity, tempUnit, presUnit); //Parameters: (float& pressure, float& temp, float& humidity, bool celsius = false, uint8_t pressureUnit = 0x0)
if (isnan(temperature)) {
#ifdef MY_DEBUG_LOCAL
Serial.println("Failed reading temperature");
#endif
} else if (temperature != lastTemp) {
// Only send temperature if it changed since the last measurement
lastTemp = temperature;
send(tempMsg.set(temperature, 1));
#ifdef MY_DEBUG_LOCAL
Serial.print("T: ");
Serial.println(temperature);
#endif
}
if (isnan(humidity)) {
#ifdef MY_DEBUG_LOCAL
Serial.println("Failed reading humidity");
#endif
} else if (humidity != lastHum) {
// Only send humidity if it changed since the last measurement
lastHum = humidity;
send(humMsg.set(humidity, 1));
#ifdef MY_DEBUG
Serial.print("H: ");
Serial.println(humidity);
#endif
}
if (isnan(pressureBme)) {
#ifdef MY_DEBUG_LOCAL
Serial.println("Failed reading pressure");
#endif
if (pressureBme != lastPressure) {
send(pressureMsg.set(pressureBme, 2));
lastPressure = pressureBme;
}
}
int forecast = sample(pressureBme);
if (pressureBme != lastPressure) {
send(pressureMsg.set(pressureBme, 2));
lastPressure = pressureBme;
}
if (forecast != lastForecast){
send(forecastMsg.set(weather[forecast]));
lastForecast = forecast;
}
sendHeartbeat();
lastSend = currentTime;
}
}
void receive(const MyMessage &message) {
}
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 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;
}