Merge pull request #261 from windkh/development
Added weather station sensor example which is a merge between the two…
This commit is contained in:
@@ -0,0 +1,510 @@
|
||||
/**
|
||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||
* between your home built sensors/actuators and HA controller of choice.
|
||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* Copyright (C) 2013-2015 Sensnology AB
|
||||
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
|
||||
*
|
||||
* Documentation: http://www.mysensors.org
|
||||
* Support Forum: http://forum.mysensors.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
*******************************
|
||||
*
|
||||
* REVISION HISTORY
|
||||
* Version 1.0 - Henrik Ekblad
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Weather station sensor example using BMP085 and DHT-11 or DHT-22 module
|
||||
* http://www.mysensors.org/build/pressure
|
||||
* http://www.mysensors.org/build/humidity
|
||||
*
|
||||
* WIRING
|
||||
* DHT Sensor
|
||||
* PIN2 D3 Arduino
|
||||
* 10k resistor between PIN1 and PIN2
|
||||
*
|
||||
* BMP Sensor
|
||||
* SCL A5 Arduino
|
||||
* SDA A4 Arduino
|
||||
*/
|
||||
|
||||
// Enable debug prints to serial monitor
|
||||
#define MY_DEBUG
|
||||
|
||||
// Enable and select radio type attached
|
||||
#define MY_RADIO_NRF24
|
||||
//#define MY_RADIO_RFM69
|
||||
|
||||
#include <SPI.h>
|
||||
#include <MySensor.h>
|
||||
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_BMP085.h>
|
||||
|
||||
#include <DHT.h>
|
||||
|
||||
// Set this to true if you want to send values altough the values did not change.
|
||||
// This is only recommended when not running on batteries.
|
||||
const bool SEND_ALWAYS = true;
|
||||
|
||||
// Adapt this constant: set it to the altitude above sealevel at your home location.
|
||||
const float SEALEVEL = 688; // meters above sealevel
|
||||
|
||||
// Constant for the world wide average pressure
|
||||
const float SEALEVEL_PRESSURE = 1013.25;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Child sensor ids
|
||||
#define CHILD_ID_BARO 0
|
||||
#define CHILD_ID_TEMP1 1
|
||||
#define CHILD_ID_HUM 2
|
||||
#define CHILD_ID_TEMP2 3
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DHT-11/22
|
||||
#define HUMIDITY_SENSOR_DIGITAL_PIN 3
|
||||
|
||||
DHT dht;
|
||||
float lastTemp;
|
||||
float lastHum;
|
||||
boolean metric = true;
|
||||
MyMessage msgHum(CHILD_ID_HUM, V_HUM);
|
||||
MyMessage msgTemp(CHILD_ID_TEMP2, V_TEMP);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// BMP085
|
||||
unsigned long SLEEP_TIME1 = 30000; // 1 minute required for forecast algorithm
|
||||
unsigned long SLEEP_TIME2 = 30000; // 1 minute required for forecast algorithm
|
||||
|
||||
Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor
|
||||
|
||||
|
||||
/*
|
||||
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)
|
||||
*/
|
||||
|
||||
const char *weatherStrings[] = { "stable", "sunny", "cloudy", "unstable", "thunderstorm", "unknown" };
|
||||
enum FORECAST
|
||||
{
|
||||
STABLE = 0, // Stable weather
|
||||
SUNNY = 1, // Slowly rising HP stable good weather
|
||||
CLOUDY = 2, // Slowly falling Low Pressure System, stable rainy weather
|
||||
UNSTABLE = 3, // Quickly rising HP, not stable weather
|
||||
THUNDERSTORM = 4, // Quickly falling LP, Thunderstorm, not stable
|
||||
UNKNOWN = 5 // Unknown, more time needed
|
||||
};
|
||||
|
||||
const char *situationStrings[] = { "very low", "low", "normal", "high", "very high" };
|
||||
enum WEATHER_SITUATION
|
||||
{
|
||||
VERY_LOW_PRESSURE = 0, // p > -7.5hPa
|
||||
LOW_PRESSURE = 1, // p > -2.5hPa
|
||||
NORMAL_PRESSURE = 2, // p < +/-2.5hPa
|
||||
HIGH_PRESSURE = 3, // p > +2.5hPa
|
||||
VERY_HIGH_PRESSURE = 4, // p > +7.5hPa
|
||||
};
|
||||
|
||||
float lastPressure = -1;
|
||||
float lastPressureTemp = -1;
|
||||
int lastForecast = -1;
|
||||
int lastSituation = NORMAL_PRESSURE;
|
||||
|
||||
const int LAST_SAMPLES_COUNT = 5;
|
||||
float lastPressureSamples[LAST_SAMPLES_COUNT];
|
||||
|
||||
// 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 tempMsg(CHILD_ID_TEMP1, V_TEMP);
|
||||
MyMessage pressureMsg(CHILD_ID_BARO, V_PRESSURE);
|
||||
MyMessage forecastMsg(CHILD_ID_BARO, V_FORECAST);
|
||||
MyMessage situationMsg(CHILD_ID_BARO, V_VAR1);
|
||||
MyMessage forecastMsg2(CHILD_ID_BARO, V_VAR2);
|
||||
|
||||
|
||||
void initPressureSensor()
|
||||
{
|
||||
if (!bmp.begin())
|
||||
{
|
||||
Serial.println(F("Could not find a valid BMP085 sensor, check wiring!"));
|
||||
while (1) {}
|
||||
}
|
||||
}
|
||||
|
||||
void initHumiditySensor()
|
||||
{
|
||||
dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
initPressureSensor();
|
||||
initHumiditySensor();
|
||||
metric = getConfig().isMetric;
|
||||
}
|
||||
|
||||
void presentation()
|
||||
{
|
||||
sendSketchInfo("Weather Station Sensor", "1.0");
|
||||
|
||||
present(CHILD_ID_BARO, S_BARO);
|
||||
present(CHILD_ID_TEMP1, S_TEMP);
|
||||
present(CHILD_ID_HUM, S_HUM);
|
||||
present(CHILD_ID_TEMP2, S_TEMP);
|
||||
}
|
||||
|
||||
int getWeatherSituation(float pressure)
|
||||
{
|
||||
int situation = NORMAL_PRESSURE;
|
||||
|
||||
float delta = pressure - SEALEVEL_PRESSURE;
|
||||
if (delta > 7.5)
|
||||
{
|
||||
situation = VERY_HIGH_PRESSURE;
|
||||
}
|
||||
else if (delta > 2.5)
|
||||
{
|
||||
situation = HIGH_PRESSURE;
|
||||
}
|
||||
else if (delta < -7.5)
|
||||
{
|
||||
situation = VERY_LOW_PRESSURE;
|
||||
}
|
||||
else if (delta < -2.5)
|
||||
{
|
||||
situation = LOW_PRESSURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
situation = NORMAL_PRESSURE;
|
||||
}
|
||||
|
||||
return situation;
|
||||
}
|
||||
|
||||
// The BMP provides a temperature value, too!
|
||||
bool updatePressureSensor()
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
//sealevel pressure p0 from absolute pressure.
|
||||
float pressure = bmp.readSealevelPressure(SEALEVEL) / 100.0;
|
||||
float temperature = bmp.readTemperature();
|
||||
if (!metric)
|
||||
{
|
||||
// Convert to fahrenheit
|
||||
temperature = temperature * 9.0 / 5.0 + 32.0;
|
||||
}
|
||||
|
||||
int forecast = sample(pressure);
|
||||
int situation = getWeatherSituation(pressure);
|
||||
|
||||
|
||||
if (SEND_ALWAYS || (temperature != lastPressureTemp))
|
||||
{
|
||||
changed = true;
|
||||
lastPressureTemp = temperature;
|
||||
Serial.print(F("Temperature = "));
|
||||
Serial.print(temperature);
|
||||
Serial.println(metric ? F(" *C") : F(" *F"));
|
||||
if (!send(tempMsg.set(lastPressureTemp, 1)))
|
||||
{
|
||||
lastPressureTemp = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (SEND_ALWAYS || (pressure != lastPressure))
|
||||
{
|
||||
changed = true;
|
||||
lastPressure = pressure;
|
||||
Serial.print(F("sealevel Pressure = "));
|
||||
Serial.print(pressure);
|
||||
Serial.println(F(" hPa"));
|
||||
if (!send(pressureMsg.set(lastPressure, 1)))
|
||||
{
|
||||
lastPressure = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (SEND_ALWAYS || (forecast != lastForecast))
|
||||
{
|
||||
changed = true;
|
||||
lastForecast = forecast;
|
||||
Serial.print(F("Forecast = "));
|
||||
Serial.println(weatherStrings[forecast]);
|
||||
if (send(forecastMsg.set(weatherStrings[lastForecast])))
|
||||
{
|
||||
if (!send(forecastMsg2.set(lastForecast)))
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lastForecast = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (SEND_ALWAYS || (situation != lastSituation))
|
||||
{
|
||||
changed = true;
|
||||
lastSituation = situation;
|
||||
Serial.print(F("Situation = "));
|
||||
Serial.println(situationStrings[situation]);
|
||||
if (!send(situationMsg.set(lastSituation, 0)))
|
||||
{
|
||||
lastSituation = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
// The dht provides a temperature, too!
|
||||
bool updateHumiditySensor()
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
float temperature = dht.getTemperature();
|
||||
float humidity = dht.getHumidity();
|
||||
|
||||
if (!isnan(temperature))
|
||||
{
|
||||
if (SEND_ALWAYS || (temperature != lastTemp))
|
||||
{
|
||||
lastTemp = temperature;
|
||||
if (!metric)
|
||||
{
|
||||
temperature = dht.toFahrenheit(temperature);
|
||||
}
|
||||
|
||||
changed = true;
|
||||
Serial.print(F("T: "));
|
||||
Serial.println(temperature);
|
||||
if (!send(msgTemp.set(temperature, 1)))
|
||||
{
|
||||
lastTemp = -1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Failed reading temperature from DHT"));
|
||||
}
|
||||
|
||||
|
||||
if (!isnan(humidity))
|
||||
{
|
||||
if (SEND_ALWAYS || (humidity != lastHum))
|
||||
{
|
||||
lastHum = humidity;
|
||||
changed = true;
|
||||
Serial.print(F("H: "));
|
||||
Serial.println(humidity);
|
||||
if (!send(msgHum.set(lastHum, 1)))
|
||||
{
|
||||
lastHum = -1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Failed reading humidity from DHT"));
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
updatePressureSensor();
|
||||
sleep(SLEEP_TIME1);
|
||||
updateHumiditySensor();
|
||||
sleep(SLEEP_TIME2);
|
||||
}
|
||||
|
||||
|
||||
float getLastPressureSamplesAverage()
|
||||
{
|
||||
float lastPressureSamplesAverage = 0;
|
||||
for (int i = 0; i < LAST_SAMPLES_COUNT; i++)
|
||||
{
|
||||
lastPressureSamplesAverage += lastPressureSamples[i];
|
||||
}
|
||||
|
||||
lastPressureSamplesAverage /= LAST_SAMPLES_COUNT;
|
||||
|
||||
// Uncomment when dubugging
|
||||
//Serial.print(F("### 5min-Average:"));
|
||||
//Serial.print(lastPressureSamplesAverage);
|
||||
//Serial.println(F(" hPa"));
|
||||
|
||||
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 dubugging
|
||||
// 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(weatherStrings[forecast]);
|
||||
|
||||
return forecast;
|
||||
}
|
||||
Reference in New Issue
Block a user