/** * 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; bool 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; #ifdef MY_DEBUG Serial.print(F("Temperature = ")); Serial.print(temperature); Serial.println(metric ? F(" *C") : F(" *F")); #endif if (!send(tempMsg.set(lastPressureTemp, 1))) { lastPressureTemp = -1.0; } } if (SEND_ALWAYS || (pressure != lastPressure)) { changed = true; lastPressure = pressure; #ifdef MY_DEBUG Serial.print(F("sealevel Pressure = ")); Serial.print(pressure); Serial.println(F(" hPa")); #endif if (!send(pressureMsg.set(lastPressure, 1))) { lastPressure = -1.0; } } if (SEND_ALWAYS || (forecast != lastForecast)) { changed = true; lastForecast = forecast; #ifdef MY_DEBUG Serial.print(F("Forecast = ")); Serial.println(weatherStrings[forecast]); #endif if (send(forecastMsg.set(weatherStrings[lastForecast]))) { if (!send(forecastMsg2.set(lastForecast))) { } } else { lastForecast = -1.0; } } if (SEND_ALWAYS || (situation != lastSituation)) { changed = true; lastSituation = situation; #ifdef MY_DEBUG Serial.print(F("Situation = ")); Serial.println(situationStrings[situation]); #endif 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; #ifdef MY_DEBUG Serial.print(F("T: ")); Serial.println(temperature); #endif 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; #ifdef MY_DEBUG Serial.print(F("H: ")); Serial.println(humidity); #endif 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; }