From 60645f04f114f38c95d6e1071f9e4b93b1bfa7a8 Mon Sep 17 00:00:00 2001 From: windkh Date: Sun, 22 Nov 2015 14:08:42 +0100 Subject: [PATCH] Added weather station sensor example which is a merge between the two examples http://www.mysensors.org/build/humidity and http://www.mysensors.org/build/pressure In addition to that the current weather situation is transmitted. Right now the temperature values of both sensors are transmitted to the gateway. This is a bit redundant, but maybe useful when mounting one the sensor (e.g. BMP) inside the building and the other one (DHT) outside of the building. --- .../WeatherStationSensor.ino | 510 ++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 libraries/MySensors/examples/WeatherStationSensor/WeatherStationSensor.ino diff --git a/libraries/MySensors/examples/WeatherStationSensor/WeatherStationSensor.ino b/libraries/MySensors/examples/WeatherStationSensor/WeatherStationSensor.ino new file mode 100644 index 0000000..bf3499e --- /dev/null +++ b/libraries/MySensors/examples/WeatherStationSensor/WeatherStationSensor.ino @@ -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 +* 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 +#include + +#include +#include + +#include + +// 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; + 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; +}