Refactored forecast algorithm
- reduced footprint by storing only the last few samples instead of all 180 - fixed wrong calculation: 65/1023 factor replaced with a conversion factor hPa to kPa - fixed error when 180 minutes were reached: sketch started at 5 min instead of 6 triggering a wrong average calculation Cleaned up code - removed unused variable - fixed unit Pa to hPa in serial print - introduced constants and enum for better readability Signed-off-by: windkh <karl-heinz.wind@web.de> I agree to the CLA!
This commit is contained in:
@@ -6,7 +6,21 @@
|
|||||||
#define BARO_CHILD 0
|
#define BARO_CHILD 0
|
||||||
#define TEMP_CHILD 1
|
#define TEMP_CHILD 1
|
||||||
|
|
||||||
unsigned long SLEEP_TIME = 60000; // Sleep time between reads (in seconds)
|
const float ALTITUDE = 688; // <-- adapt this value to your own location's altitude.
|
||||||
|
|
||||||
|
// Sleep time between reads (in seconds). Do not change this value as the forecast algorithm needs a sample every minute.
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
|
||||||
Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor
|
Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor
|
||||||
MySensor gw;
|
MySensor gw;
|
||||||
@@ -14,206 +28,229 @@ MySensor gw;
|
|||||||
float lastPressure = -1;
|
float lastPressure = -1;
|
||||||
float lastTemp = -1;
|
float lastTemp = -1;
|
||||||
int lastForecast = -1;
|
int lastForecast = -1;
|
||||||
const char *weather[] = {"stable","sunny","cloudy","unstable","thunderstorm","unknown"};
|
|
||||||
int minutes;
|
const int LAST_SAMPLES_COUNT = 5;
|
||||||
float pressureSamples[7][6];
|
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;
|
int minuteCount = 0;
|
||||||
bool firstRound = true;
|
bool firstRound = true;
|
||||||
float pressureAvg[7];
|
float pressureAvg[7];
|
||||||
|
|
||||||
float dP_dt;
|
float dP_dt;
|
||||||
boolean metric;
|
boolean metric;
|
||||||
MyMessage tempMsg(TEMP_CHILD, V_TEMP);
|
MyMessage tempMsg(TEMP_CHILD, V_TEMP);
|
||||||
MyMessage pressureMsg(BARO_CHILD, V_PRESSURE);
|
MyMessage pressureMsg(BARO_CHILD, V_PRESSURE);
|
||||||
MyMessage forecastMsg(BARO_CHILD, V_FORECAST);
|
MyMessage forecastMsg(BARO_CHILD, V_FORECAST);
|
||||||
|
|
||||||
void setup() {
|
|
||||||
gw.begin();
|
|
||||||
|
|
||||||
// Send the sketch version information to the gateway and Controller
|
void setup()
|
||||||
gw.sendSketchInfo("Pressure Sensor", "1.0");
|
{
|
||||||
|
gw.begin();
|
||||||
|
|
||||||
if (!bmp.begin()) {
|
// Send the sketch version information to the gateway and Controller
|
||||||
Serial.println("Could not find a valid BMP085 sensor, check wiring!");
|
gw.sendSketchInfo("Pressure Sensor", "1.1");
|
||||||
while (1) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register sensors to gw (they will be created as child devices)
|
if (!bmp.begin())
|
||||||
gw.present(BARO_CHILD, S_BARO);
|
{
|
||||||
gw.present(TEMP_CHILD, S_TEMP);
|
Serial.println("Could not find a valid BMP085 sensor, check wiring!");
|
||||||
metric = gw.getConfig().isMetric;
|
while (1) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register sensors to gw (they will be created as child devices)
|
||||||
|
gw.present(BARO_CHILD, S_BARO);
|
||||||
|
gw.present(TEMP_CHILD, S_TEMP);
|
||||||
|
metric = gw.getConfig().isMetric;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop()
|
||||||
float pressure = bmp.readSealevelPressure(205)/100.0; // 205 meters above sealevel
|
{
|
||||||
float temperature = bmp.readTemperature();
|
float pressure = bmp.readSealevelPressure(ALTITUDE) / 100.0;
|
||||||
|
float temperature = bmp.readTemperature();
|
||||||
|
|
||||||
if (!metric) {
|
if (!metric)
|
||||||
// Convert to fahrenheit
|
{
|
||||||
temperature = temperature * 9.0 / 5.0 + 32.0;
|
// Convert to fahrenheit
|
||||||
}
|
temperature = temperature * 9.0 / 5.0 + 32.0;
|
||||||
|
}
|
||||||
|
|
||||||
int forecast = sample(pressure);
|
int forecast = sample(pressure);
|
||||||
|
|
||||||
Serial.print("Temperature = ");
|
Serial.print("Temperature = ");
|
||||||
Serial.print(temperature);
|
Serial.print(temperature);
|
||||||
Serial.println(metric?" *C":" *F");
|
Serial.println(metric ? " *C" : " *F");
|
||||||
Serial.print("Pressure = ");
|
Serial.print("Pressure = ");
|
||||||
Serial.print(pressure);
|
Serial.print(pressure);
|
||||||
Serial.println(" Pa");
|
Serial.println(" hPa");
|
||||||
Serial.println(weather[forecast]);
|
Serial.print("Forecast = ");
|
||||||
|
Serial.println(weather[forecast]);
|
||||||
|
|
||||||
|
|
||||||
if (temperature != lastTemp) {
|
if (temperature != lastTemp)
|
||||||
gw.send(tempMsg.set(temperature,1));
|
{
|
||||||
lastTemp = temperature;
|
gw.send(tempMsg.set(temperature, 1));
|
||||||
}
|
lastTemp = temperature;
|
||||||
|
}
|
||||||
|
|
||||||
if (pressure != lastPressure) {
|
if (pressure != lastPressure)
|
||||||
gw.send(pressureMsg.set(pressure, 0));
|
{
|
||||||
lastPressure = pressure;
|
gw.send(pressureMsg.set(pressure, 0));
|
||||||
}
|
lastPressure = pressure;
|
||||||
|
}
|
||||||
|
|
||||||
if (forecast != lastForecast) {
|
if (forecast != lastForecast)
|
||||||
gw.send(forecastMsg.set(weather[forecast]));
|
{
|
||||||
lastForecast = forecast;
|
gw.send(forecastMsg.set(weather[forecast]));
|
||||||
}
|
lastForecast = forecast;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
gw.sleep(SLEEP_TIME);
|
||||||
DP/Dt explanation
|
|
||||||
|
|
||||||
0 = "Stable Weather Pattern"
|
|
||||||
1 = "Slowly rising Good Weather", "Clear/Sunny "
|
|
||||||
2 = "Slowly falling L-Pressure ", "Cloudy/Rain "
|
|
||||||
3 = "Quickly rising H-Press", "Not Stable"
|
|
||||||
4 = "Quickly falling L-Press", "Thunderstorm"
|
|
||||||
5 = "Unknown (More Time needed)
|
|
||||||
*/
|
|
||||||
|
|
||||||
gw.sleep(SLEEP_TIME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sample(float pressure) {
|
float getLastPressureSamplesAverage()
|
||||||
// Algorithm found here
|
{
|
||||||
// http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf
|
float lastPressureSamplesAverage = 0;
|
||||||
if (minuteCount == 180)
|
for (int i = 0; i < LAST_SAMPLES_COUNT; i++)
|
||||||
minuteCount = 5;
|
{
|
||||||
|
lastPressureSamplesAverage += lastPressureSamples[i];
|
||||||
|
}
|
||||||
|
lastPressureSamplesAverage /= LAST_SAMPLES_COUNT;
|
||||||
|
|
||||||
//From 0 to 5 min.
|
return lastPressureSamplesAverage;
|
||||||
if (minuteCount <= 5){
|
}
|
||||||
pressureSamples[0][minuteCount] = pressure;
|
|
||||||
}
|
|
||||||
//From 30 to 35 min.
|
|
||||||
if ((minuteCount <= 30) && (minuteCount >= 35)){
|
|
||||||
pressureSamples[1][minuteCount - 30] = pressure;
|
|
||||||
}
|
|
||||||
//From 55 to 60 min.
|
|
||||||
if ((minuteCount <= 55) && (minuteCount >= 60)){
|
|
||||||
pressureSamples[2][minuteCount - 55] = pressure;
|
|
||||||
}
|
|
||||||
//From 90 to 95 min.
|
|
||||||
if ((minuteCount <= 90) && (minuteCount >= 95)){
|
|
||||||
pressureSamples[3][minuteCount - 90] = pressure;
|
|
||||||
}
|
|
||||||
//From 115 to 119 min.
|
|
||||||
if ((minuteCount <= 115) && (minuteCount >= 120)){
|
|
||||||
pressureSamples[4][minuteCount - 115] = pressure;
|
|
||||||
}
|
|
||||||
//From 150 to 155 min.
|
|
||||||
if ((minuteCount <= 150) && (minuteCount >= 155)){
|
|
||||||
pressureSamples[5][minuteCount - 150] = pressure;
|
|
||||||
}
|
|
||||||
//From 175 to 180 min.
|
|
||||||
if ((minuteCount <= 175) && (minuteCount >= 180)){
|
|
||||||
pressureSamples[6][minuteCount - 175] = pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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++;
|
minuteCount++;
|
||||||
|
if (minuteCount > 185)
|
||||||
|
{
|
||||||
|
minuteCount = 6;
|
||||||
|
}
|
||||||
|
|
||||||
if (minuteCount == 5) {
|
if (minuteCount == 5)
|
||||||
// Avg pressure in first 5 min, value averaged from 0 to 5 min.
|
{
|
||||||
pressureAvg[0] = ((pressureSamples[0][0] + pressureSamples[0][1]
|
pressureAvg[0] = getLastPressureSamplesAverage();
|
||||||
+ pressureSamples[0][2] + pressureSamples[0][3]
|
}
|
||||||
+ pressureSamples[0][4] + pressureSamples[0][5]) / 6);
|
else if (minuteCount == 35)
|
||||||
} else if (minuteCount == 35) {
|
{
|
||||||
// Avg pressure in 30 min, value averaged from 0 to 5 min.
|
pressureAvg[1] = getLastPressureSamplesAverage();
|
||||||
pressureAvg[1] = ((pressureSamples[1][0] + pressureSamples[1][1]
|
float change = (pressureAvg[1] - pressureAvg[0]) * CONVERSION_FACTOR;
|
||||||
+ pressureSamples[1][2] + pressureSamples[1][3]
|
|
||||||
+ pressureSamples[1][4] + pressureSamples[1][5]) / 6);
|
|
||||||
float change = (pressureAvg[1] - pressureAvg[0]);
|
|
||||||
if (firstRound) // first time initial 3 hour
|
if (firstRound) // first time initial 3 hour
|
||||||
dP_dt = ((65.0 / 1023.0) * 2 * change); // note this is for t = 0.5hour
|
{
|
||||||
|
dP_dt = change * 2; // note this is for t = 0.5hour
|
||||||
|
}
|
||||||
else
|
else
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 1.5); // divide by 1.5 as this is the difference in time from 0 value.
|
{
|
||||||
} else if (minuteCount == 60) {
|
dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value.
|
||||||
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
|
}
|
||||||
pressureAvg[2] = ((pressureSamples[2][0] + pressureSamples[2][1]
|
}
|
||||||
+ pressureSamples[2][2] + pressureSamples[2][3]
|
else if (minuteCount == 65)
|
||||||
+ pressureSamples[2][4] + pressureSamples[2][5]) / 6);
|
{
|
||||||
float change = (pressureAvg[2] - pressureAvg[0]);
|
pressureAvg[2] = getLastPressureSamplesAverage();
|
||||||
|
float change = (pressureAvg[2] - pressureAvg[0]) * CONVERSION_FACTOR;
|
||||||
if (firstRound) //first time initial 3 hour
|
if (firstRound) //first time initial 3 hour
|
||||||
dP_dt = ((65.0 / 1023.0) * change); //note this is for t = 1 hour
|
{
|
||||||
|
dP_dt = change; //note this is for t = 1 hour
|
||||||
|
}
|
||||||
else
|
else
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 2); //divide by 2 as this is the difference in time from 0 value
|
{
|
||||||
} else if (minuteCount == 95) {
|
dP_dt = change / 2; //divide by 2 as this is the difference in time from 0 value
|
||||||
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
|
}
|
||||||
pressureAvg[3] = ((pressureSamples[3][0] + pressureSamples[3][1]
|
}
|
||||||
+ pressureSamples[3][2] + pressureSamples[3][3]
|
else if (minuteCount == 95)
|
||||||
+ pressureSamples[3][4] + pressureSamples[3][5]) / 6);
|
{
|
||||||
float change = (pressureAvg[3] - pressureAvg[0]);
|
pressureAvg[3] = getLastPressureSamplesAverage();
|
||||||
|
float change = (pressureAvg[3] - pressureAvg[0]) * CONVERSION_FACTOR;
|
||||||
if (firstRound) // first time initial 3 hour
|
if (firstRound) // first time initial 3 hour
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 1.5); // note this is for t = 1.5 hour
|
{
|
||||||
|
dP_dt = change / 1.5; // note this is for t = 1.5 hour
|
||||||
|
}
|
||||||
else
|
else
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 2.5); // divide by 2.5 as this is the difference in time from 0 value
|
{
|
||||||
} else if (minuteCount == 120) {
|
dP_dt = change / 2.5; // divide by 2.5 as this is the difference in time from 0 value
|
||||||
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
|
}
|
||||||
pressureAvg[4] = ((pressureSamples[4][0] + pressureSamples[4][1]
|
}
|
||||||
+ pressureSamples[4][2] + pressureSamples[4][3]
|
else if (minuteCount == 125)
|
||||||
+ pressureSamples[4][4] + pressureSamples[4][5]) / 6);
|
{
|
||||||
float change = (pressureAvg[4] - pressureAvg[0]);
|
pressureAvg[4] = getLastPressureSamplesAverage();
|
||||||
|
float change = (pressureAvg[4] - pressureAvg[0]) * CONVERSION_FACTOR;
|
||||||
if (firstRound) // first time initial 3 hour
|
if (firstRound) // first time initial 3 hour
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 2); // note this is for t = 2 hour
|
{
|
||||||
|
dP_dt = change / 2; // note this is for t = 2 hour
|
||||||
|
}
|
||||||
else
|
else
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 3); // divide by 3 as this is the difference in time from 0 value
|
{
|
||||||
} else if (minuteCount == 155) {
|
dP_dt = change / 3; // divide by 3 as this is the difference in time from 0 value
|
||||||
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
|
}
|
||||||
pressureAvg[5] = ((pressureSamples[5][0] + pressureSamples[5][1]
|
}
|
||||||
+ pressureSamples[5][2] + pressureSamples[5][3]
|
else if (minuteCount == 155)
|
||||||
+ pressureSamples[5][4] + pressureSamples[5][5]) / 6);
|
{
|
||||||
float change = (pressureAvg[5] - pressureAvg[0]);
|
pressureAvg[5] = getLastPressureSamplesAverage();
|
||||||
|
float change = (pressureAvg[5] - pressureAvg[0]) * CONVERSION_FACTOR;
|
||||||
if (firstRound) // first time initial 3 hour
|
if (firstRound) // first time initial 3 hour
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 2.5); // note this is for t = 2.5 hour
|
{
|
||||||
|
dP_dt = change / 2.5; // note this is for t = 2.5 hour
|
||||||
|
}
|
||||||
else
|
else
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 3.5); // divide by 3.5 as this is the difference in time from 0 value
|
{
|
||||||
} else if (minuteCount == 180) {
|
dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value
|
||||||
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
|
}
|
||||||
pressureAvg[6] = ((pressureSamples[6][0] + pressureSamples[6][1]
|
}
|
||||||
+ pressureSamples[6][2] + pressureSamples[6][3]
|
else if (minuteCount == 185)
|
||||||
+ pressureSamples[6][4] + pressureSamples[6][5]) / 6);
|
{
|
||||||
float change = (pressureAvg[6] - pressureAvg[0]);
|
pressureAvg[6] = getLastPressureSamplesAverage();
|
||||||
|
float change = (pressureAvg[6] - pressureAvg[0]) * CONVERSION_FACTOR;
|
||||||
if (firstRound) // first time initial 3 hour
|
if (firstRound) // first time initial 3 hour
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 3); // note this is for t = 3 hour
|
{
|
||||||
|
dP_dt = change / 3; // note this is for t = 3 hour
|
||||||
|
}
|
||||||
else
|
else
|
||||||
dP_dt = (((65.0 / 1023.0) * change) / 4); // divide by 4 as this is the difference in time from 0 value
|
{
|
||||||
|
dP_dt = change / 4; // divide by 4 as this is the difference in time from 0 value
|
||||||
|
}
|
||||||
pressureAvg[0] = pressureAvg[5]; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past.
|
pressureAvg[0] = pressureAvg[5]; // 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.
|
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.
|
if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval.
|
||||||
return 5; // Unknown, more time needed
|
{
|
||||||
|
forecast = UNKNOWN;
|
||||||
|
}
|
||||||
else if (dP_dt < (-0.25))
|
else if (dP_dt < (-0.25))
|
||||||
return 4; // Quickly falling LP, Thunderstorm, not stable
|
{
|
||||||
|
forecast = THUNDERSTORM;
|
||||||
|
}
|
||||||
else if (dP_dt > 0.25)
|
else if (dP_dt > 0.25)
|
||||||
return 3; // Quickly rising HP, not stable weather
|
{
|
||||||
|
forecast = UNSTABLE;
|
||||||
|
}
|
||||||
else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05)))
|
else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05)))
|
||||||
return 2; // Slowly falling Low Pressure System, stable rainy weather
|
{
|
||||||
|
forecast = CLOUDY;
|
||||||
|
}
|
||||||
else if ((dP_dt > 0.05) && (dP_dt < 0.25))
|
else if ((dP_dt > 0.05) && (dP_dt < 0.25))
|
||||||
return 1; // Slowly rising HP stable good weather
|
{
|
||||||
else if ((dP_dt > (-0.05)) && (dP_dt < 0.05))
|
forecast = SUNNY;
|
||||||
return 0; // Stable weather
|
}
|
||||||
|
else if ((dP_dt >(-0.05)) && (dP_dt < 0.05))
|
||||||
|
{
|
||||||
|
forecast = STABLE;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return 5; // Unknown
|
{
|
||||||
|
forecast = UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return forecast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user