diff --git a/libraries/MySensors/examples/RainGauge/D_RainSensor1.json b/libraries/MySensors/examples/RainGauge/D_RainSensor1.json
new file mode 100644
index 0000000..325391b
--- /dev/null
+++ b/libraries/MySensors/examples/RainGauge/D_RainSensor1.json
@@ -0,0 +1,523 @@
+{
+ "flashicon": "icons/Humidity_Sensor.swf",
+ "imgIconBody": "",
+ "imgIconDimmable": "",
+ "imgIconTurnable": "",
+ "imgIconMin": "",
+ "imgIconMax": "",
+ "halloIconsDir": "pics\/hallo",
+ "inScene": "0",
+ "DisplayStatus": {},
+ "doc_url": {
+ "doc_language": 1,
+ "doc_manual": 1,
+ "doc_version": 1,
+ "doc_platform": 0,
+ "doc_page": "sensors"
+ },
+
+ "Tabs": [
+ {
+ "Label": {
+ "lang_tag": "tabname_control",
+ "text": "Information"
+ },
+ "Position": "0",
+ "TabType": "flash",
+ "SceneGroup": [
+ {
+ "id": "1",
+ "top": "1.5",
+ "left": "0",
+ "x": "2",
+ "y": "2"
+ }
+ ],
+ "ControlGroup": [
+ {
+ "id": "1",
+ "scenegroup": "1",
+ "type": "info"
+ },
+ {
+ "id": "2",
+ "scenegroup": "1",
+ "type": "info"
+ }
+ ],
+ "Control": [
+ {
+ "ControlGroup":"1",
+ "ControlPair": "1",
+ "ControlHeader": "1",
+ "top": "1",
+ "left": "0",
+ "x": "1.25",
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_rate",
+ "text": "Rate of Rain"
+ },
+ "Display": {
+ "Top": 20,
+ "Left": 50,
+ "Width": 75,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"1",
+ "ControlPair": "1",
+ "ControlHeader": "1",
+ "top": "1",
+ "left": "1.25",
+ "x": "0.75",
+ "ControlType": "variable",
+ "Display": {
+ "Service": "urn:upnp-org:serviceId:RainSensor1",
+ "Variable": "CurrentRain",
+ "Top": 20,
+ "Left": 150,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "total_rain_unit",
+ "text": "mm/hr"
+ },
+ "Display": {
+ "Top": 20,
+ "Left": 200,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"2",
+ "ControlPair": "2",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "0",
+ "x": "1.25",
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "total_rain",
+ "text": "Rain Today:",
+ },
+ "Display": {
+ "Top": 55,
+ "Left": 50,
+ "Width": 75,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"2",
+ "ControlPair": "2",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "1.25",
+ "x": "0.75",
+ "ControlType": "variable",
+ "Display": {
+ "Service": "urn:upnp-org:serviceId:RainSensor1",
+ "Variable": "CurrentTRain",
+ "Top": 55,
+ "Left": 150,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_rate_unit",
+ "text": "mm"
+ },
+ "Display": {
+ "Top": 55,
+ "Left": 200,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"3",
+ "ControlPair": "3",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "0",
+ "x": "1.25",
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_forty",
+ "text": "Yesterday:"
+ },
+ "Display": {
+ "Top": 80,
+ "Left": 50,
+ "Width": 75,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"3",
+ "ControlPair": "3",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "1.25",
+ "x": "0.75",
+ "ControlType": "variable",
+ "Display": {
+ "Service": "urn:upnp-org:serviceId:VContainer1",
+ "Variable": "Variable2",
+ "Top": 80,
+ "Left": 150,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_unit",
+ "text": "mm"
+ },
+ "Display": {
+ "Top": 80,
+ "Left": 200,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"4",
+ "ControlPair": "4",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "0",
+ "x": "1.25",
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_seventy",
+ "text": "3 Days Ago:"
+ },
+ "Display": {
+ "Top": 105,
+ "Left": 50,
+ "Width": 75,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"4",
+ "ControlPair": "4",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "1.25",
+ "x": "0.75",
+ "ControlType": "variable",
+ "Display": {
+ "Service": "urn:upnp-org:serviceId:VContainer1",
+ "Variable": "Variable3",
+ "Top": 105,
+ "Left": 150,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_unit1",
+ "text": "mm"
+ },
+ "Display": {
+ "Top": 105,
+ "Left": 200,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"5",
+ "ControlPair": "5",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "0",
+ "x": "1.25",
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_forty",
+ "text": "4 Days Ago:"
+ },
+ "Display": {
+ "Top": 130,
+ "Left": 50,
+ "Width": 75,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"5",
+ "ControlPair": "5",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "1.25",
+ "x": "0.75",
+ "ControlType": "variable",
+ "Display": {
+ "Service": "urn:upnp-org:serviceId:VContainer1",
+ "Variable": "Variable4",
+ "Top": 130,
+ "Left": 150,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_unit2",
+ "text": "mm"
+ },
+ "Display": {
+ "Top": 130,
+ "Left": 200,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"6",
+ "ControlPair": "6",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "0",
+ "x": "1.25",
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_hundred",
+ "text": "5 Days Ago:"
+ },
+ "Display": {
+ "Top": 155,
+ "Left": 50,
+ "Width": 75,
+ "Height": 20
+ }
+ },
+ {
+ "ControlGroup":"6",
+ "ControlPair": "6",
+ "ControlHeader": "1",
+ "top": "0",
+ "left": "1.25",
+ "x": "0.75",
+ "ControlType": "variable",
+ "Display": {
+ "Service": "urn:upnp-org:serviceId:VContainer1",
+ "Variable": "Variable5",
+ "Top": 155,
+ "Left": 150,
+ "Width": 25,
+ "Height": 20
+ }
+ },
+ {
+ "ControlType": "label",
+ "Label": {
+ "lang_tag": "rain_unit3",
+ "text": "mm"
+ },
+ "Display": {
+ "Top": 155,
+ "Left": 200,
+ "Width": 25,
+ "Height": 20
+ }
+ }
+ ]
+ },
+ {
+ "Label": {
+ "lang_tag": "advanced",
+ "text": "Advanced"
+ },
+ "Position": "1",
+ "TabType": "javascript",
+ "ScriptName": "shared.js",
+ "Function": "advanced_device"
+ },
+ {
+ "Label": {
+ "lang_tag": "logs",
+ "text": "Logs"
+ },
+ "Position": "2",
+ "TabType": "javascript",
+ "ScriptName": "shared.js",
+ "Function": "device_logs"
+ },
+ {
+ "Label": {
+ "lang_tag": "notifications",
+ "text": "Notifications"
+ },
+ "Position": "3",
+ "TabType": "javascript",
+ "ScriptName": "shared.js",
+ "Function": "device_notifications"
+ }
+ ],
+ "eventList2": [
+ {
+ "id": 1,
+ "label": {
+ "lang_tag": "total_rain_goes_above",
+ "text": "Total rain goes above"
+ },
+ "serviceId": "urn:upnp-org:serviceId:RainSensor1",
+ "norepeat": "1",
+ "argumentList": [
+ {
+ "id": 1,
+ "dataType": "r8",
+ "name": "CurrentTRain",
+ "comparisson": ">",
+ "prefix": {
+ "lang_tag": "total_rain",
+ "text": "Total rain"
+ },
+ "suffix": {
+ "lang_tag": "total_rain_unit",
+ "text": "mm"
+ },
+ "HumanFriendlyText": {
+ "lang_tag": "hft_total_rain_goes_above",
+ "text": "Total rain for _DEVICE_NAME_ goes above _ARGUMENT_VALUE_"
+ }
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "label": {
+ "lang_tag": "total_rain_goes_below",
+ "text": "Total rain goes below"
+ },
+ "serviceId": "urn:upnp-org:serviceId:RainSensor1",
+ "norepeat": "1",
+ "argumentList": [
+ {
+ "id": 1,
+ "dataType": "r8",
+ "name": "CurrentTRain",
+ "comparisson": "<",
+ "prefix": {
+ "lang_tag": "total_rain",
+ "text": "Total rain"
+ },
+ "suffix": {
+ "lang_tag": "total_rain_unit",
+ "text": "mm"
+ },
+ "HumanFriendlyText": {
+ "lang_tag": "hft_total_rain_goes_below",
+ "text": "Total rain for _DEVICE_NAME_ goes below _ARGUMENT_VALUE_"
+ }
+ }
+ ]
+ },
+ {
+ "id": 3,
+ "label": {
+ "lang_tag": "rain_rate_goes_above",
+ "text": "Current rain goes above"
+ },
+ "serviceId": "urn:upnp-org:serviceId:RainSensor1",
+ "norepeat": "1",
+ "argumentList": [
+ {
+ "id": 1,
+ "dataType": "r8",
+ "name": "CurrentRain",
+ "comparisson": ">",
+ "prefix": {
+ "lang_tag": "rain_rate",
+ "text": "Current rain"
+ },
+ "suffix": {
+ "lang_tag": "rain_rate_unit",
+ "text": "mm/hr"
+ },
+ "HumanFriendlyText": {
+ "lang_tag": "hft_rain_rate_goes_above",
+ "text": "Current rain for _DEVICE_NAME_ goes above _ARGUMENT_VALUE_"
+ }
+ }
+ ]
+ },
+ {
+ "id": 4,
+ "label": {
+ "lang_tag": "rain_rate_goes_below",
+ "text": "Current rain goes below"
+ },
+ "serviceId": "urn:upnp-org:serviceId:RainSensor1",
+ "norepeat": "1",
+ "argumentList": [
+ {
+ "id": 1,
+ "dataType": "r8",
+ "name": "CurrentRain",
+ "comparisson": "<",
+ "prefix": {
+ "lang_tag": "rain_rate",
+ "text": "Current rain"
+ },
+ "suffix": {
+ "lang_tag": "rain_rate_unit",
+ "text": "mm/hr"
+ },
+ "HumanFriendlyText": {
+ "lang_tag": "hft_rain_rate_goes_below",
+ "text": "Current rain for _DEVICE_NAME_ goes below _ARGUMENT_VALUE_"
+ }
+ }
+ ]
+ },
+ {
+ "id": 5,
+ "label": {
+ "lang_tag": "battery_level_goes_below",
+ "text": "Battery level goes below"
+ },
+ "serviceId": "urn:micasaverde-com:serviceId:HaDevice1",
+ "argumentList": [
+ {
+ "id": 1,
+ "prefix": {
+ "lang_tag": "Level",
+ "text": "Level"
+ },
+ "dataType": "i4",
+ "name": "BatteryLevel",
+ "comparisson": "<",
+ "suffix": {
+ "lang_tag": "percent_sign",
+ "text": "%"
+ },
+ "HumanFriendlyText": {
+ "lang_tag": "hft_battery_level_goes_below",
+ "text": "Battery level for _DEVICE_NAME_ go below _ARGUMENT_VALUE_%"
+ }
+ }
+ ]
+ }
+ ],
+ "DeviceType": "urn:schemas-micasaverde-com:device:RainSensor:1"
+}
\ No newline at end of file
diff --git a/libraries/MySensors/examples/RainGauge/D_RainSensor1.xml b/libraries/MySensors/examples/RainGauge/D_RainSensor1.xml
new file mode 100644
index 0000000..c0a7492
--- /dev/null
+++ b/libraries/MySensors/examples/RainGauge/D_RainSensor1.xml
@@ -0,0 +1,24 @@
+
+
+
+ 1
+ 0
+
+
+ urn:schemas-micasaverde-com:device:RainSensor:1
+ 12
+ D_RainSensor1.json
+
+
+ urn:schemas-upnp-org:service:RainSensor:1
+ urn:upnp-org:serviceId:RainSensor1
+ S_RainSensor1.xml
+
+
+ urn:schemas-micasaverde-com:service:HaDevice:1
+ urn:micasaverde-com:serviceId:HaDevice1
+ S_HaDevice1.xml
+
+
+
+
\ No newline at end of file
diff --git a/libraries/MySensors/examples/RainGauge/RainGauge.ino b/libraries/MySensors/examples/RainGauge/RainGauge.ino
new file mode 100644
index 0000000..475eb0c
--- /dev/null
+++ b/libraries/MySensors/examples/RainGauge/RainGauge.ino
@@ -0,0 +1,594 @@
+/*
+ Arduino Tipping Bucket Rain Gauge
+
+ April 26, 2015
+
+ Version 1.4.1 alpha
+
+ Arduino Tipping Bucket Rain Gauge
+
+ Utilizing a tipping bucket sensor, your Vera home automation controller and the MySensors.org
+ gateway you can measure and sense local rain. This sketch will create two devices on your
+ Vera controller. One will display your total precipitation for the last 5 days. The other,
+ a sensor that changes state if there is recent rain (up to last 120 hours) above a threshold.
+ Both these settings are user definable.
+
+ There is a build overview video here: https://youtu.be/1eMfKQaLROo
+
+ This sketch features the following:
+
+ * Allows you to set the rain threshold in mm
+ * Allows you to determine the tripped indicator window up to 120 hours.
+ * Displays the last 5 days of rain in Variable1 through Variable5
+ of the Rain Sensor device
+ * Configuration changes to Sensor device updated every hour
+ * Should run on any Arduino
+ * Will retain Tripped/Not Tripped status and data in a power interruption, saving small amount
+ of data to EEPROM (Circular Buffer to maximize life of EEPROM)
+ * LED status indicator
+ * Optional Temp/Humidity (DHT-22 or DHT-11) and Light LUX (BH1750) sensors. To use, uncomment
+ #define DHT_ON and/or #define LUX_ON
+ * Optionally send total accumulation of each day's rainfall or send only individual days rainfall totals.
+ Comment out #define USE_DAILY to display individual daily rainfall.
+
+ by @BulldogLowell and @PeteWill for free public use
+
+ */
+#include
+#include
+#include
+#include
+
+#define NODE_ID 24 //or AUTO to let controller assign
+#define SKETCH_NAME "Rain Gauge"
+#define SKETCH_VERSION "1.4.1a"
+
+#define DWELL_TIME 125 // this allows for radio to come back to power after a transmission, ideally 0
+
+//#define DEBUG_ON // comment out this line to disable serial debug
+#define DHT_ON // uncomment out this line to enable DHT sensor
+#define LUX_ON // uncomment out this line to enable BH1750 sensor
+//#define USE_DAILY // displays each time segment as an accumulation of prior periods inclusive. Comment out to display individual daily rainfall totals in the variables sent to your controller.
+
+#define CALIBRATE_FACTOR 60 // amount of rain per rain bucket tip e.g. 5 is .05mm
+#define DHT_LUX_DELAY 300000 //Delay in milliseconds that the DHT and LUX sensors will wait before sending data
+
+#define CHILD_ID_RAIN_LOG 3 // Keeps track of accumulated rainfall
+#define CHILD_ID_TRIPPED_INDICATOR 4 // Indicates Tripped when rain detected
+#define EEPROM_BUFFER_LOCATION 0 // location of the EEPROM circular buffer
+#define E_BUFFER_LENGTH 240
+#define RAIN_BUCKET_SIZE 120
+
+#ifdef DEBUG_ON
+ #define DEBUG_PRINT(x) Serial.print(x)
+ #define DEBUG_PRINTLN(x) Serial.println(x)
+ #define SERIAL_START(x) Serial.begin(x)
+ #else
+ #define DEBUG_PRINT(x)
+ #define DEBUG_PRINTLN(x)
+ #define SERIAL_START(x)
+#endif
+//
+MySensor gw;
+//
+MyMessage msgRainRate(CHILD_ID_RAIN_LOG, V_RAINRATE);
+MyMessage msgRain(CHILD_ID_RAIN_LOG, V_RAIN);
+//
+MyMessage msgRainVAR1(CHILD_ID_RAIN_LOG, V_VAR1);
+MyMessage msgRainVAR2(CHILD_ID_RAIN_LOG, V_VAR2);
+MyMessage msgRainVAR3(CHILD_ID_RAIN_LOG, V_VAR3);
+MyMessage msgRainVAR4(CHILD_ID_RAIN_LOG, V_VAR4);
+MyMessage msgRainVAR5(CHILD_ID_RAIN_LOG, V_VAR5);
+//
+MyMessage msgTripped(CHILD_ID_TRIPPED_INDICATOR, V_TRIPPED);
+MyMessage msgTrippedVar1(CHILD_ID_TRIPPED_INDICATOR, V_VAR1);
+MyMessage msgTrippedVar2(CHILD_ID_TRIPPED_INDICATOR, V_VAR2);
+//
+#ifdef DHT_ON
+ #include
+ #define CHILD_ID_HUM 0
+ #define CHILD_ID_TEMP 1
+ #define HUMIDITY_SENSOR_DIGITAL_PIN 8
+ DHT dht;
+ float lastTemp;
+ float lastHum;
+ boolean metric = true;
+ MyMessage msgHum(CHILD_ID_HUM, V_HUM);
+ MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
+#endif
+//
+#ifdef LUX_ON
+ //BH1750 is connected to SCL (analog input A5) and SDA (analog input A4)
+ #include
+ #include
+ #define CHILD_ID_LIGHT 2
+ BH1750 lightSensor;
+ MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL);
+ unsigned int lastlux;
+ byte heartbeat = 10; //Used to send the light lux to gateway as soon as the device is restarted and after the DHT_LUX_DELAY has happened 10 times
+#endif
+unsigned long sensorPreviousMillis;
+int eepromIndex;
+int tipSensorPin = 3; // Pin the tipping bucket is connected to. Must be interrupt capable pin
+int ledPin = 5; // Pin the LED is connected to. PWM capable pin required
+unsigned long dataMillis;
+unsigned long serialInterval = 600000UL;
+const unsigned long oneHour = 3600000UL;
+unsigned long lastTipTime;
+unsigned long lastRainTime; //Used for rainRate calculation
+unsigned int rainBucket [RAIN_BUCKET_SIZE] ; /* 24 hours x 5 Days = 120 hours */
+unsigned int rainRate = 0;
+byte rainWindow = 72; //default rain window in hours. Will be overwritten with msgTrippedVar1.
+volatile int wasTippedBuffer = 0;
+int rainSensorThreshold = 50; //default rain sensor sensitivity in hundredths. Will be overwritten with msgTrippedVar2.
+byte state = 0;
+byte oldState = -1;
+int lastRainRate = 0;
+int lastMeasure = 0;
+boolean gotTime = false;
+byte lastHour;
+byte currentHour;
+//
+void setup()
+{
+ SERIAL_START(115200);
+ //
+ // Set up the IO
+ pinMode(tipSensorPin, INPUT_PULLUP);
+ attachInterrupt (1, sensorTipped, FALLING); // depending on location of the hall effect sensor may need CHANGE
+ pinMode(ledPin, OUTPUT);
+ digitalWrite(ledPin, HIGH);
+ //
+ //Let's get the controller talking to the Arduino
+ gw.begin(getVariables, NODE_ID);
+ gw.sendSketchInfo(SKETCH_NAME, SKETCH_VERSION);
+ gw.wait(DWELL_TIME);
+ gw.present(CHILD_ID_RAIN_LOG, S_RAIN);
+ gw.wait(DWELL_TIME);
+ gw.present(CHILD_ID_TRIPPED_INDICATOR, S_MOTION);
+ gw.wait(DWELL_TIME);
+ DEBUG_PRINTLN(F("Sensor Presentation Complete"));
+ //
+ //Sync time with the server, this will be called hourly in order to keep time from creeping with the crystal
+ //
+ unsigned long functionTimeout = millis();
+ while (timeStatus() == timeNotSet && millis() - functionTimeout < 30000UL)
+ {
+ gw.process();
+ gw.requestTime(receiveTime);
+ DEBUG_PRINTLN(F("Getting Time"));
+ gw.wait(1000); // call once per second
+ DEBUG_PRINTLN(F("."));
+ }
+ currentHour = hour();
+ lastHour = hour();
+ //
+ //retrieve from EEPROM stored values on a power cycle.
+ //
+ boolean isDataOnEeprom = false;
+ for (int i = 0; i < E_BUFFER_LENGTH; i++)
+ {
+ byte locator = gw.loadState(EEPROM_BUFFER_LOCATION + i);
+ if (locator == 0xFE) // found the EEPROM circular buffer index
+ {
+ eepromIndex = EEPROM_BUFFER_LOCATION + i;
+ DEBUG_PRINT(F("EEPROM Index "));
+ DEBUG_PRINTLN(eepromIndex);
+ //Now that we have the buffer index let's populate the rainBucket[] with data from eeprom
+ loadRainArray(eepromIndex);
+ isDataOnEeprom = true;
+ break;
+ }
+ }
+ //
+ if (!isDataOnEeprom) // Added for the first time it is run on a new Arduino
+ {
+ DEBUG_PRINTLN(F("I didn't find valid EEPROM Index, so I'm writing one to location 0"));
+ eepromIndex = EEPROM_BUFFER_LOCATION;
+ gw.saveState(eepromIndex, 0xFE);
+ gw.saveState(eepromIndex + 1, 0xFE);
+ //then I will clear out any bad data
+ for (int i = 2; i <= E_BUFFER_LENGTH; i++)
+ {
+ gw.saveState(i, 0x00);
+ }
+ }
+ dataMillis = millis();
+ lastTipTime = millis() - oneHour; //why is this -oneHour?? Doesn't millis() start at 0 when first powered on?
+ //
+ gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR1);
+ gw.wait(DWELL_TIME);
+ gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR2);
+ gw.wait(DWELL_TIME);
+ //
+#ifdef DHT_ON
+ dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
+ // Register all sensors to gw (they will be created as child devices)
+ gw.present(CHILD_ID_HUM, S_HUM);
+ gw.wait(DWELL_TIME);
+ gw.present(CHILD_ID_TEMP, S_TEMP);
+ gw.wait(DWELL_TIME);
+ metric = gw.getConfig().isMetric;
+ gw.wait(DWELL_TIME);
+#endif
+ //
+#ifdef LUX_ON
+ gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
+ gw.wait(DWELL_TIME);
+ lightSensor.begin();
+#endif
+ //
+ DEBUG_PRINTLN(F("Radio Setup Complete!"));
+ transmitRainData();
+}
+
+void loop()
+{
+ gw.process();
+ if (state)
+ {
+ prettyFade(); // breathe if tripped
+ }
+ else
+ {
+ slowFlash(); // blink if not tripped
+ }
+#ifdef DEBUG_ON // Serial Debug Block
+ if ( (millis() - dataMillis) >= serialInterval)
+ {
+ for (int i = 24; i <= 120; i = i + 24)
+ {
+ updateSerialData(i);
+ }
+ dataMillis = millis();
+ }
+#endif
+ //
+ // let's constantly check to see if the rain in the past rainWindow hours is greater than rainSensorThreshold
+ //
+ int measure = 0; // Check to see if we need to show sensor tripped in this block
+ for (int i = 0; i < rainWindow; i++)
+ {
+ measure += rainBucket [i];
+ if (measure != lastMeasure)
+ {
+ // DEBUG_PRINT(F("measure value (total rainBucket within rainWindow): "));
+ // DEBUG_PRINTLN(measure);
+ lastMeasure = measure;
+ }
+ }
+ //
+ state = (measure >= (rainSensorThreshold * 100));
+ if (state != oldState)
+ {
+ gw.send(msgTripped.set(state));
+ gw.wait(DWELL_TIME);
+ DEBUG_PRINT(F("New Sensor State... Sensor: "));
+ DEBUG_PRINTLN(state ? "Tripped" : "Not Tripped");
+ oldState = state;
+ }
+ //
+ unsigned long tipDelay = millis() - lastRainTime;
+ if (wasTippedBuffer) // if was tipped, then update the 24hour total and transmit to Vera
+ {
+ DEBUG_PRINTLN(F("Sensor Tipped"));
+ DEBUG_PRINT(F("rainBucket [0] value: "));
+ DEBUG_PRINTLN(rainBucket [0]);
+ gw.send(msgRain.set((float)rainTotal(currentHour) / 100, 1)); //Calculate the total rain for the day
+ gw.wait(DWELL_TIME);
+ wasTippedBuffer--;
+ rainRate = ((oneHour) / tipDelay);
+ if (rainRate != lastRainRate)
+ {
+ gw.send(msgRainRate.set(rainRate, 1));
+ gw.wait(DWELL_TIME);
+ DEBUG_PRINT(F("RainRate= "));
+ DEBUG_PRINTLN(rainRate);
+ lastRainRate = rainRate;
+ }
+ lastRainTime = lastTipTime;
+ }
+ //
+ currentHour = hour();
+ if (currentHour != lastHour)
+ {
+ DEBUG_PRINTLN(F("One hour elapsed."));
+ gw.send(msgRain.set((float)rainTotal(currentHour) / 100, 1)); // send today's rainfall
+ gw.wait(DWELL_TIME);
+ gw.saveState(eepromIndex, highByte(rainBucket[0]));
+ gw.saveState(eepromIndex + 1, lowByte(rainBucket[0]));
+ DEBUG_PRINT(F("Saving rainBucket[0] to eeprom. rainBucket[0] = "));
+ DEBUG_PRINTLN(rainBucket[0]);
+ for (int i = RAIN_BUCKET_SIZE - 1; i >= 0; i--)//cascade an hour of values back into the array
+ {
+ rainBucket [i + 1] = rainBucket [i];
+ }
+ gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR1);
+ gw.wait(DWELL_TIME);
+ gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR2);
+ gw.wait(DWELL_TIME);
+ rainBucket[0] = 0;
+ eepromIndex = eepromIndex + 2;
+ if (eepromIndex > EEPROM_BUFFER_LOCATION + E_BUFFER_LENGTH)
+ {
+ eepromIndex = EEPROM_BUFFER_LOCATION;
+ }
+ DEBUG_PRINT(F("Writing to EEPROM. Index: "));
+ DEBUG_PRINTLN(eepromIndex);
+ gw.saveState(eepromIndex, 0xFE);
+ gw.saveState(eepromIndex + 1, 0xFE);
+ gw.requestTime(receiveTime); // sync the time every hour
+ gw.wait(DWELL_TIME);
+ transmitRainData();
+ rainRate = 0;
+ gw.send(msgRainRate.set(rainRate, 1));
+ gw.wait(DWELL_TIME);
+ DEBUG_PRINTLN(F("Sending rainRate is 0 to controller"));
+ lastHour = currentHour;
+ }
+ if (millis() - sensorPreviousMillis > DHT_LUX_DELAY)
+ {
+ #ifdef DHT_ON //DHT Code
+ doDHT();
+ #endif
+ #ifdef LUX_ON
+ doLUX();
+ #endif
+ sensorPreviousMillis = millis();
+ }
+}
+//
+#ifdef DHT_ON
+void doDHT(void)
+{
+ float temperature = dht.getTemperature();
+ if (isnan(temperature))
+ {
+ DEBUG_PRINTLN(F("Failed reading temperature from DHT"));
+ } else if (temperature != lastTemp)
+ {
+ lastTemp = temperature;
+ if (!metric)
+ {
+ temperature = dht.toFahrenheit(temperature);
+ }
+ gw.send(msgTemp.set(temperature, 1));
+ gw.wait(DWELL_TIME);
+ DEBUG_PRINT(F("Temperature is: "));
+ DEBUG_PRINTLN(temperature);
+ }
+ float humidity = dht.getHumidity();;
+ if (isnan(humidity))
+ {
+ DEBUG_PRINTLN(F("Failed reading humidity from DHT"));
+ } else if (humidity != lastHum)
+ {
+ lastHum = humidity;
+ gw.send(msgHum.set(humidity, 1));
+ gw.wait(DWELL_TIME);
+ DEBUG_PRINT(F("Humidity is: "));
+ DEBUG_PRINTLN(humidity);
+ }
+}
+#endif
+//
+#ifdef LUX_ON
+void doLUX(void)
+{
+ unsigned int lux = lightSensor.readLightLevel();// Get Lux value
+ DEBUG_PRINT(F("Current LUX Level: "));
+ DEBUG_PRINTLN(lux);
+ heartbeat++;
+ if (lux != lastlux || heartbeat > 10)
+ {
+ gw.send(msg.set(lux));
+ lastlux = lux;
+ }
+ if (heartbeat > 10)
+ {
+ heartbeat = 0;
+ }
+}
+#endif
+//
+void sensorTipped()
+{
+ unsigned long thisTipTime = millis();
+ if (thisTipTime - lastTipTime > 100UL)
+ {
+ rainBucket[0] += CALIBRATE_FACTOR; // adds CALIBRATE_FACTOR hundredths of unit each tip
+ wasTippedBuffer++;
+ }
+ lastTipTime = thisTipTime;
+}
+//
+int rainTotal(int hours)
+{
+ int total = 0;
+ for ( int i = 0; i <= hours; i++)
+ {
+ total += rainBucket [i];
+ }
+ return total;
+}
+
+void updateSerialData(int x)
+{
+ DEBUG_PRINT(F("Rain last "));
+ DEBUG_PRINT(x);
+ DEBUG_PRINTLN(F(" hours: "));
+ float tipCount = 0;
+ for (int i = 0; i < x; i++)
+ {
+ tipCount = tipCount + rainBucket [i];
+ }
+ tipCount = tipCount / 100;
+ DEBUG_PRINTLN(tipCount);
+}
+
+void loadRainArray(int value) // retrieve stored rain array from EEPROM on powerup
+{
+ for (int i = 0; i < RAIN_BUCKET_SIZE; i++)
+ {
+ value = value - 2;
+ if (value < EEPROM_BUFFER_LOCATION)
+ {
+ value = EEPROM_BUFFER_LOCATION + E_BUFFER_LENGTH;
+ }
+ DEBUG_PRINT(F("EEPROM location: "));
+ DEBUG_PRINTLN(value);
+ byte rainValueHigh = gw.loadState(value);
+ byte rainValueLow = gw.loadState(value + 1);
+ unsigned int rainValue = rainValueHigh << 8;
+ rainValue |= rainValueLow;
+ rainBucket[i + 1] = rainValue;
+ //
+ DEBUG_PRINT(F("rainBucket[ value: "));
+ DEBUG_PRINT(i + 1);
+ DEBUG_PRINT(F("] value: "));
+ DEBUG_PRINTLN(rainBucket[i + 1]);
+ }
+}
+
+void transmitRainData(void)
+{
+ DEBUG_PRINT(F("In transmitRainData. currentHour = "));
+ DEBUG_PRINTLN(currentHour);
+ int rainUpdateTotal = 0;
+ for (int i = currentHour; i >= 0; i--)
+ {
+ rainUpdateTotal += rainBucket[i];
+ DEBUG_PRINT(F("Adding rainBucket["));
+ DEBUG_PRINT(i);
+ DEBUG_PRINTLN(F("] to rainUpdateTotal."));
+ }
+ DEBUG_PRINT(F("TX Day 1: rainUpdateTotal = "));
+ DEBUG_PRINTLN((float)rainUpdateTotal / 100.0);
+ gw.send(msgRainVAR1.set((float)rainUpdateTotal / 100.0, 1)); //Send current day rain totals (resets at midnight)
+ gw.wait(DWELL_TIME);
+#ifdef USE_DAILY
+ rainUpdateTotal = 0;
+#endif
+ for (int i = currentHour + 24; i > currentHour; i--)
+ {
+ rainUpdateTotal += rainBucket[i];
+ DEBUG_PRINT(F("Adding rainBucket["));
+ DEBUG_PRINT(i);
+ DEBUG_PRINTLN(F("] to rainUpdateTotal."));
+ }
+ DEBUG_PRINT(F("TX Day 2: rainUpdateTotal = "));
+ DEBUG_PRINTLN((float)rainUpdateTotal / 100.0);
+ gw.send(msgRainVAR2.set((float)rainUpdateTotal / 100.0, 1));
+ gw.wait(DWELL_TIME);
+#ifdef USE_DAILY
+ rainUpdateTotal = 0;
+#endif
+ for (int i = currentHour + 48; i > currentHour + 24; i--)
+ {
+ rainUpdateTotal += rainBucket[i];
+ DEBUG_PRINT(F("Adding rainBucket["));
+ DEBUG_PRINT(i);
+ DEBUG_PRINTLN(F("] to rainUpdateTotal."));
+ }
+ DEBUG_PRINT(F("TX Day 3: rainUpdateTotal = "));
+ DEBUG_PRINTLN((float)rainUpdateTotal / 100.0);
+ gw.send(msgRainVAR3.set((float)rainUpdateTotal / 100.0, 1));
+ gw.wait(DWELL_TIME);
+#ifdef USE_DAILY
+ rainUpdateTotal = 0;
+#endif
+ for (int i = currentHour + 72; i > currentHour + 48; i--)
+ {
+ rainUpdateTotal += rainBucket[i];
+ DEBUG_PRINT(F("Adding rainBucket["));
+ DEBUG_PRINT(i);
+ DEBUG_PRINTLN(F("] to rainUpdateTotal."));
+ }
+ DEBUG_PRINT(F("TX Day 4: rainUpdateTotal = "));
+ DEBUG_PRINTLN((float)rainUpdateTotal / 100.0);
+ gw.send(msgRainVAR4.set((float)rainUpdateTotal / 100.0, 1));
+ gw.wait(DWELL_TIME);
+#ifdef USE_DAILY
+ rainUpdateTotal = 0;
+#endif
+ for (int i = currentHour + 96; i > currentHour + 72; i--)
+ {
+ rainUpdateTotal += rainBucket[i];
+ DEBUG_PRINT(F("Adding rainBucket["));
+ DEBUG_PRINT(i);
+ DEBUG_PRINTLN(F("] to rainUpdateTotal."));
+ }
+ DEBUG_PRINT(F("TX Day 5: rainUpdateTotal = "));
+ DEBUG_PRINTLN((float)rainUpdateTotal / 100.0);
+ gw.send(msgRainVAR5.set((float)rainUpdateTotal / 100.0, 1));
+ gw.wait(DWELL_TIME);
+}
+
+void getVariables(const MyMessage &message)
+{
+ if (message.sensor == CHILD_ID_RAIN_LOG)
+ {
+ // nothing to do here
+ }
+ else if (message.sensor == CHILD_ID_TRIPPED_INDICATOR)
+ {
+ if (message.type == V_VAR1)
+ {
+ rainWindow = atoi(message.data);
+ if (rainWindow > 120)
+ {
+ rainWindow = 120;
+ }
+ else if (rainWindow < 1)
+ {
+ rainWindow = 1;
+ }
+ if (rainWindow != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera
+ {
+ gw.send(msgTrippedVar1.set(rainWindow));
+ }
+ }
+ else if (message.type == V_VAR2)
+ {
+ rainSensorThreshold = atoi(message.data);
+ if (rainSensorThreshold > 10000)
+ {
+ rainSensorThreshold = 10000;
+ }
+ else if (rainSensorThreshold < 1)
+ {
+ rainSensorThreshold = 1;
+ }
+ if (rainSensorThreshold != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera
+ {
+ gw.send(msgTrippedVar2.set(rainSensorThreshold));
+ }
+ }
+ }
+}
+
+void prettyFade(void)
+{
+ float val = (exp(sin(millis() / 2000.0 * PI)) - 0.36787944) * 108.0;
+ analogWrite(ledPin, val);
+}
+
+void slowFlash(void)
+{
+ static boolean ledState = true;
+ static unsigned long pulseStart = millis();
+ if (millis() - pulseStart < 100UL)
+ {
+ digitalWrite(ledPin, !ledState);
+ pulseStart = millis();
+ }
+}
+
+void receiveTime(unsigned long time)
+{
+ DEBUG_PRINTLN(F("Time received..."));
+ setTime(time);
+ char theTime[6];
+ sprintf(theTime, "%d:%2d", hour(), minute());
+ DEBUG_PRINTLN(theTime);
+}
diff --git a/libraries/MySensors/examples/RainGauge/S_Rainsensor1.xml b/libraries/MySensors/examples/RainGauge/S_Rainsensor1.xml
new file mode 100644
index 0000000..f812265
--- /dev/null
+++ b/libraries/MySensors/examples/RainGauge/S_Rainsensor1.xml
@@ -0,0 +1,43 @@
+
+
+
+ 1
+ 0
+
+
+
+ CurrentTRain
+ yes
+ r8
+ total_rain
+
+
+ CurrentRain
+ yes
+ r8
+ rain_rate
+
+
+
+
+ GetCurrentTRain
+
+
+ RainValue
+ out
+ CurrentTRain
+
+
+
+
+ GetCurrentRain
+
+
+ RainValue
+ out
+ CurrentRain
+
+
+
+
+
\ No newline at end of file