Navigation

    • Register
    • Login
    • OpenHardware.io
    • Categories
    • Recent
    • Tags
    • Popular
    1. Home
    2. BulldogLowell
    3. Best
    • Profile
    • Following
    • Followers
    • Topics
    • Posts
    • Best
    • Groups

    Best posts made by BulldogLowell

    • Irrigation Controller (up to 16 valves with Shift Registers)

      I put together an extension of the multi-Relay controller for use as a controller for your irrigation project if you have more zones than available pins on your Arduino.

      This sketch features the following:

      • Allows you to cycle through All zones or individual zone control.
      • Use the (n+1)th device to activate each zone in numeric sequence (zero to n) using
        Variable1 as the "ON" time in minutes in each of the vera devices created.
      • Use the individual zone controller to activate a single zone. This feature uses
        Variable2 as the "ON" time for each individual device/zone.
      • Connect according to pinout in the sketch and uses an 74HC595 (or equiv) Shift Register as to
        allow the MySensors standard radio configuration and still leave available digital pins
      • Compiles to ~12,000 Bytes, so will run on any Arduino
      • Turning on any zone will stop the current process and begin that particular process.
      • Turning off any zone will stop the current process and turn off all zones.
      • Sketch must collect your desired intervals so it takes several minutes to startup.
      • If you change your desired time intervals for your zones, simply restart your arduino
        and it will self update to reflect those changes.

      Example, I am using with 8 relays:

      This will create 9 devices. Zero through 7 are the individual relays. Eight is the Sequencer, so to speak (refer to attachment).

      Once you create this and add it using the gateway, go to each of zero through 7 and edit Variable1 and Variable2 for what time you want to use for the Sequencer or Zone respectively. Then save the settings. Then, restart your arduino; your arduino will extract these settings and save them to an array.

      When you turn on device 8 (aka the Sequencer) the relays will actuate in order from zero to seven, each one staying on for the period entered in the Variable1 field. There is a 5 second delay at the start of a new zone to allow for the valves to hydraulically reset.

      When you turn on any of devices zero through 7, it will run that zone only for the period of time entered in Variable2.

      Selecting any new zone (0-8) will stop the current process and start as per above.

      Hope you have a use for it. If you see any opportunity to improve, or find a bug, let me know.

      Jim
      modified. Attached wrong file, whoops!

      Sprinkler.ino

      posted in My Project
      BulldogLowell
      BulldogLowell
    • DailyTimer library for Arduino

      I recently updated my DailyTimer library for Arduino which provides tools to set daily timers for control of devices such as lamps, appliances, etc. I hope someone else will find this useful. There is also a Particle version.

      I developed this primarily for automatically (device level) firing time-based events on certain days of the week (configurable).

      Any contribution is welcome.

      Something about the library:

      A library that will allow you to control daily events.

      Provides tools to set daily timers for control of devices such as lamps, appliances, etc. Developed primarilary for houshold presence simulation.

      Allows for setting ON and OFF times, days of the week (i.e. weekends, weekdays, Fridays) and the use of random on/off times.

      Timers may bridge midnight, simply enter times accordingly.

      Can automatically set correct timer state on powerup or schedule changes.

      Random start and/or end times.

      Randomly set days of week.

      Select custom days of the week.

      Set a timed event with just the start time as a trigger.

      you can return the active days (e.g. in the case of setting random days).

      Dynamically set your start or end time (i.e. using some type of celestial function or web or server call to determine Sunrise or Sunset times)

      posted in Development
      BulldogLowell
      BulldogLowell
    • Vera Helper with Arduino

      I've been interested in augmenting my network with Ethernet and WiFi devices for some time. So, just messing around with an Uno and an Ethernet Shield, I built a device that can (without the RF integration)

      • Control any Vera device through HTTP Commands

      • Can be controlled by VERA luup or any wget style command

      • be a sensor on my network

      • be an independant/agnostic layer with equal footing with My Sensors

      • be dependable

      I'm not all the way there yet, but i built a sensor that can talk to my Vera, controlling vera devices and can be controlled by vera.

      This is the web page it creates:

      Screen Shot 2014-12-06 at 3.46.02 PM.png

      this sketch allow you to turn on/off a relay via a webpage or Vera, and control a vera device.

      take a look:

      ArduinoEthernetNode – 01:29
      — Jim B

      #include <Wire.h>
      #include <EEPROM.h>
      #include <SPI.h>         
      #include <Ethernet.h>
      #include <EthernetUdp.h>
      #include <utility/w5100.h>
      #include <Time.h>
      #include <Timezone.h>
      #include <LiquidCrystal_I2C.h>
      #include "DHT.h"
      
      //#define DEBUG_ON
      #define LED_PIN 8
      #define DHT_SENSOR_PIN 7
      
      #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
      //
      boolean lightOn;
      DHT dht;
      //
      LiquidCrystal_I2C lcd(0x27, 16, 2);
      uint8_t clock[8] = {0x0,0xe,0x15,0x17,0x11,0xe,0x0}; // I'm-fetching-time-indicator for LCD display
      //
      EthernetUDP Udp;
      EthernetServer server(80);
      uint8_t mac[] = { 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE };
      unsigned int localPort = 8888;
      IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov NTP server  // IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov NTP server  // IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov NTP server
      const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
      byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 
      byte socketStat[MAX_SOCK_NUM];
      //
      char myString[100];
      byte locator = 0;
      EthernetClient client;
      const byte myserver[] = { 192,168,1,59 };
      //
      const char *dayOfWeek[] = {"Null","Sunday ","Monday ", "Tuesday ", "Wednesday ", "Thursday ", "Friday ", "Saturday "};
      const char *monthString[] = {"NULL", "January", "February", "March", "April", "May", "June", "July", "August","September", "October", "November", "December"};
      // 
      TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240};  //Eastern Daylight Time = UTC - 4 hours
      TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300};   //Eastern Standard Time = UTC - 5 hours
      Timezone usET(usEDT, usEST);
      TimeChangeRule *tcr;
      boolean forceClockUpdate = true;
      //
      void setup() 
      {
        SERIAL_START(9600);
        lightOn = EEPROM.read(0);
        pinMode(LED_PIN, OUTPUT);
        dht.setup(DHT_SENSOR_PIN);
        digitalWrite(LED_PIN, lightOn);
        //
        DEBUG_PRINTLN(F("configuring ethernet"));
        if (Ethernet.begin(mac) == 0) // start Ethernet and UDP
        {
          DEBUG_PRINTLN(F("Failed to configure Ethernet using DHCP")); 
          while(true){}
        }
        server.begin();
        Udp.begin(localPort);
        //
        lcd.init();
        lcd.clear();
        lightOn? lcd.backlight() : lcd.noBacklight();
        lcd.createChar(0, clock);
      }
      //
      void loop()
      {
        updateLCD();
        getNTPtime();
        webControl();
      }
      //
      void updateLCD()
      {
        static int lastSecond; 
        time_t rightNow = now();
        if (second(rightNow) != lastSecond)
        {
          lcd.setCursor(0,0);
          lcd.print(F("Time:"));
          DEBUG_PRINT(F("Time:"));
          lcd.print(hourFormat12(rightNow) < 10 ? F(" ") : F(""));
          DEBUG_PRINT(hourFormat12(rightNow) < 10 ? F(" ") : F(""));
          lcd.print(hourFormat12(rightNow));
          DEBUG_PRINT(hourFormat12(rightNow));
          lcd.print(minute(rightNow) < 10 ? F(":0") : F(":"));
          DEBUG_PRINT(minute(rightNow) < 10 ? F(":0") : F(":"));
          lcd.print(minute(rightNow));
          DEBUG_PRINT(minute(rightNow));
          lcd.print(second(rightNow) < 10 ? F(":0") : F(":"));
          DEBUG_PRINT(second(rightNow) < 10 ? F(":0") : F(":"));
          lcd.print(second(rightNow));
          DEBUG_PRINT(second(rightNow));
          lcd.print(isAM() ? "am" : "pm");
          DEBUG_PRINT(isAM() ? " am " : " pm ");
          lcd.setCursor(0,1);
          lcd.print(dayOfWeek[weekday(rightNow)]);
          DEBUG_PRINTLN(dayOfWeek[weekday(rightNow)]);
          lcd.print(F("      "));
          lcd.setCursor(11,1);
          lcd.print(month(rightNow) < 10 ? F(" ") : F(""));
          lcd.print(month(rightNow));
          lcd.print(day(rightNow) < 10 ? F("/0") : F("/"));
          lcd.print(day(rightNow));
        }
        lastSecond = second(rightNow);
      }
      //unsigned long
      void sendNTPpacket(IPAddress& address) // Send an NTP request to the time server at the given address 
      {
        memset(packetBuffer, 0, NTP_PACKET_SIZE); 
        packetBuffer[0] = 0b11100011;   // LI, Version, Mode
        packetBuffer[1] = 0;            // Stratum, or type of clock
        packetBuffer[2] = 6;            // Polling Interval
        packetBuffer[3] = 0xEC;         // Peer Clock Precision
        packetBuffer[12]  = 49; 
        packetBuffer[13]  = 0x4E;
        packetBuffer[14]  = 49;
        packetBuffer[15]  = 52;           
        Udp.beginPacket(address, 123); //NTP requests are to port 123
        Udp.write(packetBuffer,NTP_PACKET_SIZE);
        Udp.endPacket(); 
      }
      //
      void receiveTime(unsigned long newTime)
      {
        DEBUG_PRINT(F("Time value received: "));
        int lastSecond = second();
        int lastMinute = minute();
        int lastHour = hour();
        setTime(newTime);
        if ((second() != lastSecond) || (minute() != lastMinute) || (hour() != lastHour))
        {
          DEBUG_PRINTLN(F("Clock updated...."));
          DEBUG_PRINT(F("Sensor's time currently set to:"));
          DEBUG_PRINT(hourFormat12() < 10? F(" 0") : F(" "));
          DEBUG_PRINT(hourFormat12());
          DEBUG_PRINT(minute() < 10? F(":0") : F(":"));
          DEBUG_PRINT(minute());
          DEBUG_PRINTLN(isAM()? F("am") : F("pm"));
          DEBUG_PRINT(month());
          DEBUG_PRINT(F("/"));
          DEBUG_PRINT(day());
          DEBUG_PRINT(F("/"));
          DEBUG_PRINTLN(year());
          DEBUG_PRINTLN(dayOfWeek[weekday()]);
        }
        lcd.setCursor(15,0);
        lcd.print(F(" "));
      }
      //
      void getNTPtime()
      {
        static unsigned long lastUpdateTime;
        const unsigned long interval = 60000UL;
        if ((millis() - lastUpdateTime >= interval) || forceClockUpdate)
        {
          lcd.setCursor(15,0);
          lcd.write(0);
          sendNTPpacket(timeServer); // send an NTP packet to a time server
          delay(1000);  
          if (Udp.parsePacket()) 
          {  
            Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
            unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
            unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
            unsigned long secsSince1900 = highWord << 16 | lowWord;  
            DEBUG_PRINT(F("Seconds since Jan 1 1900 = "));
            DEBUG_PRINTLN(secsSince1900);               
            DEBUG_PRINT(F("Unix time = "));
            time_t utcEpoch = secsSince1900 - 2208988800UL;//seventyYears = 2208988800UL
            DEBUG_PRINTLN(utcEpoch);                               
            receiveTime(usET.toLocal(utcEpoch, &tcr) + 2);  //about 2 seconds to call for time
          }
          if (!forceClockUpdate)
          {
            lastUpdateTime += interval;
          }
          forceClockUpdate = false;
        }
      }
      //
      void webControl()
      {
        EthernetClient client = server.available();
        if (client) 
        {
          while (client.connected()) 
          {
            if (client.available()) 
            {
              char c = client.read();
              if (locator < 100)
              {
                myString[locator] = c;
                locator++;
                myString[locator] = '\0'; 
              }
              if (c == '\n') //if HTTP request has ended
              {
                DEBUG_PRINTLN(F("MyString ="));
                DEBUG_PRINTLN(myString);
                client.println(F("HTTP/1.1 200 OK")); //new page
                client.println(F("Content-Type: text/html"));
                client.println();
                client.println(F("<HTML>"));
                client.println(F("<HEAD>"));
                client.println(F("<meta name='apple-mobile-web-app-capable' content='yes' />"));
                client.println(F("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />"));
                client.println(F("<link rel='stylesheet' type='text/css' href='http://homeautocss.net84.net/a.css' />"));
                client.println(F("<TITLE>Jim's Home Automation</TITLE>"));
                client.println(F("</HEAD>"));
                client.println(F("<BODY>"));
                client.println(F("<H1>NTP Clock/Light</H1>"));
                client.println(F("<hr />"));
                client.println(F("<br />"));
                client.println(F("<a href=\"/?ledOn\"\">Light On</a>"));
                client.println(F("<a href=\"/?ledOff\"\">Light Off</a><br />"));  
                client.println(F("<br />"));
                client.println(F("<br />"));
                client.println(F("<a href=\"/?phoneyOn\"\">PhoneyTV On</a>"));
                client.println(F("<a href=\"/?phoneyOff\"\">PhoneyTV Off</a><br />"));  
                client.println(F("<br />"));
                client.println(F("<br />"));
                client.println(F("<a href=\"/?syncClock\"\">Force Synchronize Clock</a>"));
                client.println(F("<br />"));
                client.println(F("<br />"));
                client.println(F("<hr />"));
                client.println(F("<H2>Current Conditions</H2>"));
                client.println(F("<H3>"));
                client.println(dayOfWeek[weekday()]);
                client.println(F(","));
                client.println(monthString[month()]);
                client.println(F(" "));
                client.println(day());
                client.println(F(","));
                client.println(year());
                client.println(F("<br />"));
                client.println(F("<br />"));
                client.println(F("<font color=\"blue\">Temperature</font>"));
                client.println(F("<font color=\"red\">"));
                client.println((int)floor(dht.toFahrenheit(dht.getTemperature()) + 0.5), 1);
                client.println(F("</font>"));
                client.println(F("<font color=\"blue\">F</font>"));
                client.println(F("<br />"));
                client.println(F("<H3><font color=\"blue\">Humidity</font>"));
                client.println(F("<font color=\"red\">"));
                client.println((int)floor(dht.getHumidity() + 0.5), 1);
                client.println(F("</font>"));
                client.println(F("<font color=\"blue\">%</font>"));
                client.println(F("<br />"));
                client.println(F("<div style=\"background-color:grey;"));
                client.print(F(" color:black; margin:20px; padding:20px;\">"));
                client.println(F("<h3>Jim Brower</h3>"));
                client.println(F("<p>"));
                client.println("&#169");
                client.println(F(" 2014"));
                client.println(F("</p>"));
                client.println(F("</div>"));
                client.println(F("</BODY>"));
                client.println(F("</HTML>"));
                delay(1);
                client.stop();
                if(strstr(myString, "?ledOn")) // control arduino pin and LCD backlight //checks for 'on'
                {
                  lightOn = true;
                  EEPROM.write(0,0xFF);
                  digitalWrite(LED_PIN, HIGH);
                  lcd.backlight();
                  DEBUG_PRINTLN(F("Led On"));
                }
                if(strstr(myString, "?ledOff")) // compliment to above
                {
                  lightOn = false;
                  EEPROM.write(0,0x00);
                  digitalWrite(LED_PIN, LOW);
                  lcd.noBacklight();
                  DEBUG_PRINTLN(F("Led Off"));
                }
                else if (strstr(myString, "?syncClock"))
                {
                  forceClockUpdate = true;
                  DEBUG_PRINTLN(F("Sync Set"));
                }
                else if (strstr(myString, "?phoneyOn"))
                {
                  phoneyTV(true);
                  DEBUG_PRINTLN(F("PhoneyTV On"));
                }
                else if (strstr(myString, "?phoneyOff"))
                {
                  phoneyTV(false);
                  DEBUG_PRINTLN(F("PhoneyTV Off"));
                }
                myString[0] = '\0'; //clearing string for next read
                locator = 0;
              }
            }
          }
        }
      }
      //
      void phoneyTV(boolean status)
      {
        ShowSockStatus();
        if (client.connect(myserver, 3480)) //starts client connection, checks for connection
        {  
          DEBUG_PRINTLN(F("connected"));
          client.print(F("GET /data_request?"));
          client.print(F("id=action&output_format=xml"));
          client.print(F("&DeviceNum=88"));
          client.print(F("&serviceId=urn:upnp-org:"));
          client.print(F("serviceId:SwitchPower1"));
          client.print(F("&action=SetTarget"));
          client.print(F("&newTargetValue="));
          client.print(status ? F("1") : F("0"));
          client.println(F(" HTTP/1.1"));
          client.println(F("Connection: close"));  //close 1.1 persistent connection  
          client.println(); //end of get request
          delay(1);
          client.stop();
        }
      }
      
      void ShowSockStatus()
      {
        for (int i = 0; i < MAX_SOCK_NUM; i++) {
          DEBUG_PRINT(F("Socket#"));
          DEBUG_PRINT(i);
          uint8_t s = W5100.readSnSR(i);
          socketStat[i] = s;
          DEBUG_PRINT(F(":0x"));
          #ifdef DEBUG_ON
          Serial.print(s,16);
          #endif
          DEBUG_PRINT(F(" "));
          DEBUG_PRINT(W5100.readSnPORT(i));
          DEBUG_PRINT(F(" D:"));
          uint8_t dip[4];
          W5100.readSnDIPR(i, dip);
          for (int j=0; j<4; j++) {
            Serial.print(dip[j],10);
            if (j<3) Serial.print(".");
          }
          DEBUG_PRINT(F("("));
          DEBUG_PRINT(W5100.readSnDPORT(i));
          DEBUG_PRINT(F(")"));
        }
      }
      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Best dimmable countertop lights?

      @mfalkvidd

      I used these on Amazon. By and large the reviews are spot on... but I installed inside a molding strip that I routed out like this and mounted underneath, to make it have a finished look. You can see the molding in the photo below, just inboard of the doors. I used a clear adhesive to augment the not-great adhesive backing that the strips come with, something like a bead of hot glue down each side of the milled out molding.

      0_1458770884571_FullSizeRender.jpg
      and the finished product like this... though the color is horribly off on my iPhone photo. It looks much warmer to the eye in real life.
      0_1458771159855_FullSizeRender-1.jpg
      Most importantly, they allow for a lot of light and a happy wife!

      Photo from the other side shows the LED's above as well, though not completed for the left hand island:
      0_1458772094442_FullSizeRender-2.jpg
      Again, the Colors are all wrong!!

      posted in Hardware
      BulldogLowell
      BulldogLowell
    • Web App for Simple Control of my Home Automation kludge

      Like a lot of forum members, I have a lot of automation in my house. It sometimes creates issues for guests who don't have a familiarity with home automation and my wife!

      I don't want to give total control of the house to guests, but I wanted them to have a tool that will allow them to control my hybrid system (Vera and a bunch of Particle Photon controlled devices and other gadgets) without having to download an app, which would allow them total control of my house and I have to enter my security keys.

      So, I wrote a web app which is hosted on my mac mini server and it is accessible to anyone I let onto my private LAN, only while they are on the LAN.

      It took me a while to do this, having never done anything in HTML/CSS/JavaScript/PHP, but it turns out that it is all pretty easy to do. The most difficult part was getting the status data from Vera, because of its particular preferences of handling cross-domain Ajax calls. I eventually created a PHP proxy and well, it all came together after that.

      when the website loads, it makess an Ajax call to my Vera and updates the state of every device. Plus, it also gets the state of each of the Particle Photon devices I can control/monitor. I also go get all of the local weather conditions, as you can see below.

      If anyone is interested in working on something similar or building off of this project, I'm happy to share my code. I'm certain that it would offer a lot of utility to the MySensors crowd, especially where they are using Vera's ugly UI.

      Here are a few screen shots off my web browser and iOS device:

      different devices including control of my thermostats (they can only control within a range) and dimmer in the outdoor kitchen:

      0_1472135355783_Screen Shot 2016-08-25 at 10.15.27.png

      Another shot including the drop down "Zoom to Room" menu:

      0_1472135410798_Screen Shot 2016-08-25 at 10.16.27.png

      Local Conditions:

      0_1472135865680_Screen Shot 2016-08-25 at 10.36.59.png

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: What did you build today (Pictures) ?

      @dbemowsk said in What did you build today (Pictures) ?:

      Seeing the falling stars at the end of the Adafruit test code made me think of rain or snow falling. Something like that might go outside the limits of the pro minis memory, but hey, never hurts to try.

      Hey Nice Work!

      I see you are using I2C, vs SPI... that may be why your display is so much slower than the demo in the Adafruit video.

      Can't wait to see the final board!

      posted in General Discussion
      BulldogLowell
      BulldogLowell
    • RE: Waterproof Temp Sensor with dht humidty/temp sensor

      @cleight

      try this...

      start here:

      boolean metric = true; 
      

      with

      boolean metric = false;
      

      and comment out this line:

      metric = gw.getConfig().isMetric;
      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Web App for Simple Control of my Home Automation kludge

      @TheoL

      So, my little app connects to Particle devices using their RESTful API, Ajax looks like this, which control my PhoneyTV (Particle version here) and a LED dimmer:

      example of a getter:

      function getParticleData(){
      	var requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/" + getFunc + "/?access_token=" + accessToken;
      	$.getJSON(requestURL, function(json) {
      		document.getElementById("range").innerHTML = json.result + "%";
      		document.getElementById("myRange").value = json.result;
      	});
      	var myURL = "https://api.spark.io/v1/devices/" + phoneyID + "/" + phoneyVariable + "/?access_token=" + accessToken;
      	$.getJSON(myURL, function(data) {
      	var state = parseInt(data.result);
      	console.log("phoneyTV state =" + state);
      	document.getElementById("phoneyTV").checked = ((state == 1)? true : false);
      	});
      }
      

      and a setter:

      function sendDimmerValue() {
          var newValue = document.getElementById("range").innerHTML;
          var request = new XMLHttpRequest();
          var data = 'params=' + newValue + '&access_token=' + accessToken;
          var url = 'https://api.particle.io/v1/devices/' + dev_id + '/setDimLevel/';
          request.open('POST', url, true);
          request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
          request.send(data);
      }
      

      no extra url encoding needed here but for Vera you must, for example:

      function sendThermostatValue(tStat) {
      	console.log("pressed");
          var newValue = document.getElementById(tStat.reading).innerHTML;
          var request = new XMLHttpRequest();
          var url = 'http://10.0.1.15/proxy.php?url=http%3A%2F%2F10.0.1.25%3A3480%2Fdata_request%3Fid%3Daction%26output_format%3Dxml%26DeviceNum%3D' + tStat.deviceNum + '%26serviceId%3Durn%3Aupnp-org%3AserviceId%3ATemperatureSetpoint1_Cool%26action%3DSetCurrentSetpoint%26NewCurrentSetpoint%3D' + newValue;
      	console.log(url);
          request.open('GET', url);
          request.send();
      }
      

      My code doesn't dynamically create devices (yet) rather, it is static HTML. My next improvement would be to build device classes, poll Vera for relevant device types and build the UI dynamically.

      @sundberg84,

      I started with a simple web page with a single value... once I learned how to make an AJAX call to Vera, it was easy to call any variable I wanted, especially because of Vera's native JSON output.

      If Domoticz API supports JSON, well it will be a breeze for you.

      Since there are a few people interested I can post something. If we get a Vera person interested, the Javascript I wrote will give them a big step in the right direction.

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: IR Switch for Luminara Candle Automation (repost with video, photos and final sketch)

      Wow this is an old topic!

      But... I was working on a project and thought that anyone who was looking for a NeoPixel candle example that isn't blocking, well this would be the place. It flickers and flutters like a real candle. The only thing is that the flame isn't illuminated like these nice Luminaras, but I'm too cheap to buy them!

      Keep in mind that my example here uses GRB LEDs, but you merely need to reorder the variables in order to get this working for RGB. Plus the Time Library is not Arduino, so you will have to play with that too for a good randomSeed().

      It looks pretty realistic with even a single neoPixel in a sheet of A4 paper rolled into a cylinder. I'll post a video when I can.

      Have fun with it:

      #include "neopixel.h"
      
      enum CandleStates{
        BURN_CANDLE,
        FLICKER_CANDLE,
        FLUTTER_CANDLE,
        MODES_MAX_CANDLE
      };
      
      enum PixelSelect{
        EVERY_PIXEL,
        SINGLE_PIXEL,
      };
      
      class Candle : public Adafruit_NeoPixel
      {
        public:
          Candle(uint16_t count, uint8_t pin, uint8_t type);
          Candle(uint16_t count, uint8_t pin, uint8_t type, PixelSelect pixel, uint32_t pixNum = 0);
          ~Candle(){};
          void update();
      
        private:
          bool fire(uint8_t greenDropValue, uint32_t cycleTime);
      
          PixelSelect _pixelMode = EVERY_PIXEL;
          uint32_t _pixNum = 0;
          CandleStates _mode;
          uint32_t _lastModeChange;
          uint32_t _modeDuration;
      
          uint8_t _redPx = 255;
          uint8_t _bluePx = 10; //10 for 5v, 15 for 3.3v
          uint8_t _grnHigh = 100; //110-120 for 5v, 135 for 3.3v
          uint8_t _grnPx = 100;
      
          uint32_t _lastBurnUpdate = 0;
          int _direction = 1;
      };
      
      Candle::Candle(uint16_t count, uint8_t pin, uint8_t type) : Adafruit_NeoPixel(count, pin, type)
      {
        randomSeed(Time.now() + micros());
        _mode = BURN_CANDLE;
      }
      
      Candle::Candle(uint16_t count, uint8_t pin, uint8_t type, PixelSelect pixel, uint32_t pixNum) : Adafruit_NeoPixel(count, pin, type)
      {
        _pixelMode = pixel;
        _pixNum = pixNum;
      }
      
      void Candle::update()
      {
        if(millis() - _lastModeChange > _modeDuration)
        {
          _mode = static_cast<CandleStates>(random(MODES_MAX_CANDLE));
          _modeDuration = random(1000, 8000);
          _lastModeChange = millis();
          //Serial.printlnf("New state: %d\tTime: %d", static_cast<int>(_mode), _modeDuration);
        }
        switch(_mode)
        {
          case BURN_CANDLE:
            this->fire(10, 120);
            break;
          case FLICKER_CANDLE:
            this->fire(15, 120);
            break;
          case FLUTTER_CANDLE:
            this->fire(30, 120);
            break;
        };
      }
      
      bool Candle::fire(uint8_t greenDropValue, uint32_t cycleTime)
      {
        int currentMillis = millis();
        if(currentMillis - _lastBurnUpdate > (cycleTime / greenDropValue / 2))
        {
          _grnPx = constrain(_grnPx += _direction, _grnHigh - greenDropValue, _grnHigh);
          if(_grnPx == _grnHigh - greenDropValue or _grnPx == _grnHigh)
          {
            _direction *= -1;
          }
          switch (_pixelMode)
          {
            case EVERY_PIXEL:
              for(int i = 0; i < this->numPixels(); i++)
              {
                this->setPixelColor(i, _grnPx, _redPx, _bluePx);
              }
              break;
            case SINGLE_PIXEL:
              this->setPixelColor(_pixNum, _grnPx, _redPx, _bluePx);
              break;
          }
          this->show();
          _lastBurnUpdate = currentMillis;
        }
      }
      
      #define PIXEL_COUNT 2
      #define PIXEL_PIN D2
      #define PIXEL_TYPE WS2812B    // I'M USING GRB WS2821B's here
      
      Candle candle = Candle(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE, SINGLE_PIXEL);
      
      void setup(void)
      {
        Serial.begin(115200);
        pinMode(13, OUTPUT);
        candle.begin();
        candle.show();
        Serial.println("Started program");
      }
      
      void loop(void)
      {
        candle.update();
        static uint32_t lastFlashMillis = 0;
        if(millis() - lastFlashMillis > 250)
        {
          digitalWrite(13, !digitalRead(13));
          lastFlashMillis = millis();
        }
      }
      
      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Getting numbers from Vera to a sensor

      @axillent

      Again, thanks for the help.

      I attached some photos of the display if anyone is interested. The sketch has not been simplified test, but it works so if you are at all interested, you have a good place to start:

      // Humidity and Temperature Sensor with LCD Display
      // Retrieves Temperature, Humidity and today's Hi/Low from Vera and displays it on an LCD alongside with Room Temperature and Humidity.
      //Updates Vera with Temp and Humidity just like any other sensor
      #include <Sleep_n0m1.h>
      #include <SPI.h>
      #include <EEPROM.h>  
      #include <RF24.h>
      #include <Sensor.h>  
      #include <DHT.h> 
      #include <Wire.h> 
      #include <LiquidCrystal_I2C.h>
      //
      LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x20 for a 16 chars and 2 line display
      //
      #define CHILD_ID_HUM 0
      #define CHILD_ID_TEMP 1
      #define HUMIDITY_SENSOR_DIGITAL_PIN 4
      unsigned long SLEEP_TIME = 5; // Sleep time between reads (in seconds)
      //
      Sensor gw;
      DHT dht;
      Sleep sleep;
      float lastTemp;
      float lastHum;
      float temperature;
      float humidity;
      float temp;
      boolean metric = false;
      int OutdoorHumidity;
      int OutdoorTemp;
      int TodayLow;
      int TodayHigh;
      int counter = 60;
      //
      void setup()  
      { 
        Serial.begin(9600);
        gw.begin();
        dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN); 
        gw.sendSketchInfo("Humidity", "1.0");
        gw.sendSensorPresentation(CHILD_ID_HUM, S_HUM); 
        gw.sendSensorPresentation(CHILD_ID_TEMP, S_TEMP);
        gw.getStatus(CHILD_ID_HUM, V_VAR1);
        gw.getStatus(CHILD_ID_TEMP,V_VAR1);//current exterior temperature
        gw.getStatus(CHILD_ID_TEMP,V_VAR2);//today's high temperature
        gw.getStatus(CHILD_ID_TEMP,V_VAR3);//today's low temperature
        //
        lcd.init(); // initialize the lcd 
        // Print a message to the LCD.
        lcd.backlight();
        lcd.setCursor(0,0);
        lcd.print("    Welcome!");
        lcd.setCursor(0,1);
        lcd.print("      Home");
        delay(2000);
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("    Wireless");
        lcd.setCursor(0,1);
        lcd.print("  WeatherWatch");
        delay(2000);
      }
      //
      void loop()      
      {  
        delay(dht.getMinimumSamplingPeriod());
        if (counter == 60){
          gw.getStatus(CHILD_ID_HUM, V_VAR1);
          HumStatus(gw.getMessage());
          gw.getStatus(CHILD_ID_TEMP, V_VAR1);
          TempStatus(gw.getMessage());
          gw.getStatus(CHILD_ID_TEMP, V_VAR2);
          LowStatus(gw.getMessage());
          gw.getStatus(CHILD_ID_TEMP, V_VAR3);
          HighStatus(gw.getMessage());
          counter = 0;
        }
        temperature = dht.getTemperature();
        temp = dht.toFahrenheit(temperature);
        if (isnan(temperature)) {
            Serial.println("Failed reading temperature from DHT");
        } else if (temperature != lastTemp) {
          lastTemp = temperature;
           if (!metric) {
            temperature = dht.toFahrenheit(temperature);
          }
          gw.sendVariable(CHILD_ID_TEMP, V_TEMP, temperature, 1);
            Serial.print("T: ");
            Serial.println(temperature);
        }
        humidity = dht.getHumidity();
        if (isnan(humidity)) {
            Serial.println("Failed reading humidity from DHT");
        } else if (humidity != lastHum) {
             lastHum = humidity;
            gw.sendVariable(CHILD_ID_HUM, V_HUM, humidity, 1);
            Serial.print("H: ");
            Serial.println(humidity);
        }
        // Power down the radio.  Note that the radio will get powered back up
        // on the next write() call.
        delay(1000); //delay to allow serial to fully print before sleep
        gw.powerDown();
        sleep.pwrDownMode(); //set sleep mode
        sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
        updateOutsideTemp();
        updateOutsideHumid();
        delay(3000);
        updateLow();
        updateHigh();
        delay(3000);
        updateTemp();
        updateHumid();
        counter = (counter + 1);
      }
      void updateTemp(){
        lcd.setCursor(0,0);
        lcd.print("Temperature: ");
        lcd.print(round(temp));
        lcd.print("F ");
      }
      void updateLow(){
        lcd.setCursor(0,0);
        lcd.print("Today's Low: ");
        lcd.print(round(TodayLow));
        lcd.print("F ");
      }
      void updateHigh(){
        lcd.setCursor(0,1);
        lcd.print("       High: ");
        lcd.print(round(TodayHigh));
        lcd.print("F ");
      }
      void updateHumid(){
        lcd.setCursor(0,1);
        lcd.print("Humidity:    ");
        lcd.print(round(humidity));
        lcd.print("% ");
      }
      void updateOutsideTemp(){
        lcd.setCursor(0,0);
        lcd.print("Outside Temp:");
        lcd.print(OutdoorTemp);
        lcd.print("F ");
      }
      void updateOutsideHumid(){
        //getStatus(CHILD_ID_HUM, V_VAR1);
        lcd.setCursor(0,1);
        lcd.print("Humidity:    ");
        lcd.print(OutdoorHumidity);
        lcd.print("% ");
      }
      void HumStatus(message_s message){
        if (message.header.type==V_VAR1) {
           OutdoorHumidity = atoi(message.data);
         }
      }
      void TempStatus(message_s message){
        if (message.header.type==V_VAR1) {
           OutdoorTemp = atoi(message.data);
         }
      }
      void LowStatus(message_s message){
        if (message.header.type==V_VAR2) {
           TodayLow = atoi(message.data);
         }
      }
      void HighStatus(message_s message){
        if (message.header.type==V_VAR3) {
           TodayHigh = atoi(message.data);
         }
      }
      

      Have fun with it and let me know if you have any ideas to improve or take it further!

      photo.JPG
      photo2.JPG
      photo3.JPG

      posted in Hardware
      BulldogLowell
      BulldogLowell
    • RE: [closed] How to differentiate Arduino Pro Mini 3v3 and 5v?

      Or very easily...

      apply between 5 and 12VDC to Vin and ground (attach to USB)

      Read voltage at Vcc and ground...

      the 3.3V pro mini will read 3.3V, and if it is a 5V, well you will read 5.

      posted in General Discussion
      BulldogLowell
      BulldogLowell
    • Mailbox Sensor

      I mentioned this on the Vera site, thought I'd post some Photos.

      I get a pouch notification when the mailman makes his delivery.

      I finally have it working with the off the shelf Digital light sensor sketch and PLEG to notice the light change.

      photo 1-1.JPG photo 2-1.JPG photo 3-1.JPG photo 4-1.JPG photo 5.PNG

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: The new ESP8266 WIFI models

      @ServiceXp

      Just received a few, and will start messing with them while on holidays... I've seen progress on a few threads out there.

      posted in Hardware
      BulldogLowell
      BulldogLowell
    • RE: Hi everyone

      @candylion

      Hi! most of us here have an interest in home automation, computers, sensors, hobby electronics and such.

      that's why we are hanging out at the MySensors forum, not flying to Ibiza to hang with David Gates while listening to Green Day. 🙂

      posted in General Discussion
      BulldogLowell
      BulldogLowell
    • PhoneyTV for Vera is Here!

      So here is a fun project for you to build... introducing PhoneyTV for Vera.

      PhoneyTV is a Home Automation connected security device that mimics the ambient light produced by a television. This makes your house appear to be occupied and will hopefully deter would-be robbers from trying to enter your house.

      Some highlights:

      • Utilizes the MySensors basic sketch for relays, therefore controllable by Vera.
        Energy efficient versus actually leaving a TV on. My 12x 1/2watt LEDs are burning average of about 5 watts with the PWM... a nightlight basically.
        It I can be BRIGHT with 12 10mm LED's produces the light of a 50in LCD TV, in my observation.
        Utilizes 6 channels featuring 3 PWM pins in order to vary the intensity of blues and whites.
        Uses no Delays, rather it uses a timer to produce its random flickering while maintaining terrific radio connection.
        Is customizable to allow you to adjust your PhoneyTV to behave to your specific preferences.
        Is EASY to make... It can be 100% powered by your Arduino and with exception of the actual LED's (I used 12pcs of 1/2watt super-bright 10mm LED's) only requires a board and a few resistors (be careful with this red LEDs).
        It can be powered with an old cell phone charger or any 7-12V power supply.
        You can create a custom, fun housing to camouflage or highlight your PhoneyTV. I repurposed a box that has been sitting on a shelf empty for a while. It is nice because when it is not in use, it can be invisible.
        You can breadboard it on your UNO in under 45 minutes and depending on your box, build in in one evening.
        It can be improved... While this produces a pretty realistic effect, nothing is preventing you from adding more to it like dimming or even multiplexing.
        It is inexpensive versus buying something like FakeTV, where after $30, you will still have to add Home Automation ready outlet if you want to control it. I used a Nano, but you could try a ProMini and save a few bucks.
        Is expandable. Why not add a Thermometer, Hygrometer or Barometer? For a few more dollars you can monitor the room you keep your Phoney TV in.

      I have attached some photos of the approach I took to build the physical box, of course there are many possibilities for you to consider and build.

      I'll post the sketch in an update...

      I want to give credit to DeltaNu1142 for the inspiration and the idea, thanks!

      photo 3-5.JPG photo 4-5.JPG photo 2-3.JPG photo 3-4.JPG photo 4-4.JPG photo 5-1.JPG photo 3-3.JPG photo 4-3.JPG photo 1-2.JPG photo 2-2.JPG photo 3-2.JPG photo 4-2.JPG

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Power usage with a Pro Mini as sensor node(Humidity & Temperature!)

      @EasyIoT if you poll every hour, it will outlive Noah. (OK, maybe Shem)

      posted in Hardware
      BulldogLowell
      BulldogLowell
    • Another famous and talented Ekblad!!!

      Screen Shot 2015-06-24 at 10.06.25 PM.png

      posted in General Discussion
      BulldogLowell
      BulldogLowell
    • Weather Station with Scene Activator!!!

      you will have a lot of fun building this one.

      ADDED: Check out the youtube video.

      This Weather Station will display your indoor and outdoor environmental conditions but features some hot capabilities...

      Send a Short Message to the LCD from Vera by adding another variable and populating it with whatever you like on Vera.

      Using it in the bedroom and you want to turn the backlight off to get a good night sleep? It has that. Just use scenes or PLEG to toggle a variable on the hygrometer device to turn it on or off at specified times.

      Want to trigger a scene or initiate some other action from PLEG? It has that. Just use PLEG or Luup to monitor a variable on the hygrometer device to do cool things like:

      Put your house into Night mode
      Turn off all your lights
      Open your window coverings
      Turn on your Stereo, TV, or even a tea kettle
      Close your Garage door...
      in fact... this little button can be set up to do whatever you can setup in Vera!!!

      I have attached the sketch, so you are just a few tiny components away from having this all on your nightstand or on your desk.

      1: You will need to hardware debounce your switch but can be done with two extra components available for just a few shekels at your local electronics supplier. diagram attached.
      2: Arduino Nano, Uno or Pro Mini.
      3: Hygrometer/thermometer sensor
      4: LCD display I got mine on ebay
      5: A few wires
      6: A Button
      7: Moisture/Temperature sensor like this

      If you plan on building a simple hygrometer/thermometer... build this one instead, and make it really cool and powerful.

      Have Fun!

      *6-May EDIT
      I was having a couple small issues still with the debounce so added it in the sketch. I took the humidity and temp and put into a function and added the call the to setup. I didn't notice that it was delayed quite a bit getting T&H.
      *
      Debounce.pngWeatherDisplayPBSceneController.ino

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Aluminium Case for MySensors

      @allanvbcrew

      aluminum is paramagnetic, meaning it will become magnetic when exposed to magnetic fields. So, it can affect the transmission of radio waves.

      However, it is only slightly paramagnetic, but if it is doped with certain other metals (it is common to alloy aluminum to make it easier to extrude, die-cast or otherwise form) it may affect the degree of its paramagnetic properties.

      If you use aluminum, and it is very very thin, you may be able to transmit radio waves through just fine. It is probably better though to design in an elegant transition to plastic, wood or some other nonmagnetic material so your radio waves may penetrate.

      shorterer answer... try it and let us know 🙂

      posted in Hardware
      BulldogLowell
      BulldogLowell
    • RE: Total noob's question about building a PWM light controller

      @JoeStrout said:

      What's really bugging me, I guess, is that it seems a bit rube-goldbergish to fill my house with (1) WiFi signals at 2.4 GHz, (2) Z-Wave signals at 900 MHz, and (3) MySensors signals at 2.4 GHz again

      those waves are there already!!!

      😉

      @JoeStrout said:

      instead throw on a WiFi module and speak to it directly

      you can certainly just create a WiFi or ethernet device with Arduino that can talk to your Vera controller.

      I did a kind of scene controller that works over ethernet... dimming LEDs and all. If you want, I'll share the code...

      When you get there, you can get some help here too. A lot of forum members have created Lua code to be able to do what you want (i.e. dimmer).

      posted in General Discussion
      BulldogLowell
      BulldogLowell
    • RE: Rain Guage

      Something like this:

      I just need to do a few more CG calculations to make sure it tips:

      Screen Shot 2014-06-04 at 5.56.28 PM.png
      Screen Shot 2014-06-04 at 5.58.43 PM.png
      Screen Shot 2014-06-04 at 5.58.26 PM.png

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: **Unnamed Project** USB Hub stick

      @Anticimex said:

      I also maintain that we should try to collaborate with all hardware affiliated with MySensors. It serves no one that we try to compete with each other 😉

      No one except the market! 😉

      posted in Hardware
      BulldogLowell
      BulldogLowell
    • RE: Trying to make sense of Serial display

      @NotTooTechy said:

      but what is all the other stuff? I also mentioned I am writing my own Controller and surely that might have implied that I have been programming for a while as it is not a trivial task. As it happens that "while" is about 40 years.

      0;0;3;0;9;read: 23-23-0 s=255,c= < This mishmash
      3,t=11,pt=0,l=8,sg=0:Humidity< This mishmash
      23;255;3;0;11;Humidity< This mishmash
      0;0;3;0;9;read: 23-23-0 s=255,c=3,t=12,pt=0,l=3,sg=0:1.0< This mishmash
      23;255;3;0;12;1.0 < aaahhhh finally, data!

      but, in the API it describes exactly what the "mishmash" is! It contains Node ID's, sensor ID's and other critical data for you to sort the messages... plus the data. 😉

      posted in General Discussion
      BulldogLowell
      BulldogLowell
    • RE: Rain Guage

      @marceltrapman

      Thanks. It was actually pretty straightforward except for trying to remember how to get the Center of Gravity. In the end, the triangle method worked! Besides, you can just use this and it will take you no time 🙂

      Attached is the working print. I cannot believe that a guy 15km away did the print for $16 (plus tax). He printed it in under an hour. It is smallish, about 100mm long by 100mm high by 25mm wide assembled.

      I still have to mount a magnet to make sure it tips, but looking good. Check out the YouTube...

      Photo:

      photo 2.JPG

      Tipping Bucket Rain Guage – 00:14
      — Jim B

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: **Unnamed Project** USB Hub stick

      @Anticimex

      Exactly my point, I have a different sense of humor 😉

      As a minor supporter, I like the cooperation and team spirit.

      Money making hasn't been a priority and that makes supporting it so easy.

      posted in Hardware
      BulldogLowell
      BulldogLowell
    • RE: Good-looking way to control dimmer?

      @mfalkvidd

      I haven't tried it yet but always thought one of these distance sensors, hidden away under a counter or shelf could be a great interactive dimmer control.

      I haven't tried yet but I ought to. The programming should be relatively easy, I would think. Look for the presence of a reflection in the operating range of 2-25cm (they sell other ranges) for a time and adjust by moving nearer-to or further-from the sensor.

      Or, just wave past it to turn on/off and hold hand over it to fade up/down slowly.

      posted in General Discussion
      BulldogLowell
      BulldogLowell
    • RE: Air Quality: CO2 Sensor MH-Z14

      @epierre

      Thanks for your work on this. I am going to build 2 for my factory in China. I make construction heaters fired by diesel, kerosene, JET-A/B, and #1/#2 heating oil. I am always concerned about the employees exposure to CO buildup, even though we employ a high capacity ventilation system.

      I will let you know what the final build looks like, I envision a very high quality (machine-tool quality) build with colour changing indicator and klaxon. Something different for me, I am usually making things for my kids or the house so it has to look 'nice-ish'.

      cheers!

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Best dimmable countertop lights?

      @Samuel235 said:

      Just one of many ideas, the possibilties are endless. I'de always go for RGB leds over just white, allows for more customisation while able to still give you the same white as the white leds.

      ... unless, as in my use case, colors are not needed.

      I'm too stingy to pay for the unused dry powder!

      😉

      posted in Hardware
      BulldogLowell
      BulldogLowell
    • RE: SoCal help completing a MySensor system

      @ensbusiness

      What kind of help do you need? Are you looking for someone to help you program sensors, or are you looking for someone to program a gateway? Perhaps you can provide a little more detail.

      posted in General Discussion
      BulldogLowell
      BulldogLowell
    • RE: Irrigation Controller (up to 16 valves with Shift Registers)

      I updated the Irrigation Controller to 1.4.1 and added some more functionality. It will control up to 16 valves now, controls an LCD display and some fun new things.

      Screen Shot 2014-11-30 at 5.00.56 PM.png

      Video of the communications icons

      Take a look and I'll update when I get it all in a box:

      /*
      MySprinkler for MySensors
      
      Arduino Multi-Zone Sprinkler Control
      
      November, 2014
      
      *** Version 2.0 
      
      *** Upgraded to http://MySensors.org version 1.4.1
      *** Expanded for up to 16 Valves
      *** Setup for active low relay board or comment out #define ACTIVE_LOW to switch to active high
      *** Switch to bitshift method vs byte arrays
      *** Changed RUN_ALL_ZONES Vera device to 0 (was highest valve)
      *** Added optional LCD display featuring remaining time, date last ran & current time
      *** Features 'raindrop' and 'clock' icons which indicate sensor is updating valve times and clock respectively
      
      Utilizing your Vera home automation controller and the MySensors.org gateway you can
      control up to a sixteen zone irrigation system with only three digital pins.  This sketch 
      will create NUMBER_OF_VALVES + 1 devices on your Vera controller
      
      This sketch features the following:
      
      * Allows you to cycle through All zones (RUN_ALL_ZONES) or individual zone (RUN_SINGLE_ZONE) control.  
      * Use the 0th controller to activate RUN_ALL_ZONES (each zone in numeric sequence 1 to n) 
      using Variable1 as the "ON" time in minutes in each of the vera devices created.
      * Use the individual zone controller to activate a single zone.  This feature uses 
      Variable2 as the "ON" time for each individual device/zone.
      * Connect according to pinout below and uses Shift Registers as to allow the MySensors 
      standard radio configuration and still leave available digital pins
      * Turning on any zone will stop the current process and begin that particular process.
      * Turning off any zone will stop the current process and turn off all zones.
      * To push your new time intervals for your zones, simply change the variable on your Vera and 
      your arduino will call to Vera once a minute and update accordingly.
      * Pushbutton activation to RUN_ALL_ZONES
      * LED status indicator
      
      INSTRUCTIONS:
      
      * After assembling your arduino, radio, decoupling capacitors, shift register(s), status LED, pushbutton LCD (I2C connected to 
      A4 and A5) and relays, and load the sketch.
      * Following the instructions at https://MySensors.org include the device to your MySensors Gateway.
      * Verify that each new device has a Variable1 and Variable2. Populate data accordingly with whole minutes for 
      the RUN_ALL_ZONES routine and the RUN_SINGLE_ZONE routines.  The values entered may be zero.  
      * Once you have entered values for each zone and each variable, save the settings by pressing the red save button on your Vera.
      * Restart your arduino; verify the settings are loaded into your arduino with the serial monitor; the array will be printed
      on the serial monitor.
      * Your arduino should slow-flash, indicating that it is in ready mode.
      * There are multiple debug serial prints that can be monitored to assure that it is operating properly.
      * https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads for the I2C library, or use yours
      
      Contributed by Jim (BulldogLowell@gmail.com) and is released to the public domain
      */
      // 
      #include <Wire.h>
      #include <Time.h>
      #include <MySensor.h>
      #include <SPI.h>
      #include <LiquidCrystal_I2C.h>
      //
      #define NUMBER_OF_VALVES 8  // Change this to set your valve count up to 16.
      #define VALVE_RESET_TIME 7500UL   // Change this (in milliseconds) for the time you need your valves to hydraulically reset and change state
      #define RADIO_ID 9 // AUTO  // Change this to fix your Radio ID or use Auto
      //
      #define ACTIVE_LOW // comment out this line if your relays are active high
      //
      #define DEBUG_ON   // comment out to supress serial monitor output
      //
      #ifdef ACTIVE_LOW
      #define BITSHIFT_VALVE_NUMBER ~(1U << (valveNumber-1))
      #define ALL_VALVES_OFF 0xFFFF
      #else
      #define BITSHIFT_VALVE_NUMBER (1U << (valveNumber-1))
      #define ALL_VALVES_OFF 0U
      #endif
      //
      #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
      //
      typedef enum {
       STAND_BY_ALL_OFF, RUN_SINGLE_ZONE, RUN_ALL_ZONES, CYCLE_COMPLETE}
      SprinklerStates;
      //
      SprinklerStates state = STAND_BY_ALL_OFF;
      SprinklerStates lastState;
      //
      int allZoneTime [NUMBER_OF_VALVES + 1];
      int valveSoloTime [NUMBER_OF_VALVES + 1];
      int valveNumber;
      int lastValve;
      unsigned long startMillis;
      const int ledPin = 5;
      boolean buttonPushed = false;
      boolean showTime = true;
      boolean clockUpdating = false;
      boolean recentUpdate = true;
      const char *dayOfWeek[] = {
       "Null","Sunday ","Monday ", "Tuesday ", "Wednesday ", "Thursday ", "Friday ", "Saturday "};
      //
      time_t lastTimeRun = 0;
      //Setup Shift Register...
      const int latchPin = 8;
      const int clockPin = 4;
      const int dataPin  = 7;
      // 
      uint8_t clock[8] = {0x0,0xe,0x15,0x17,0x11,0xe,0x0}; // fetching time indicator
      uint8_t raindrop[8] = {0x4,0x4,0xA,0xA,0x11,0xE,0x0,}; // fetching Valve Data indicator
      //
      LiquidCrystal_I2C lcd(0x27, 16, 2);  // set the LCD I2C address to 0x27 (16 characters and 2 line display)
      MySensor gw;
      //
      MyMessage msg1valve(0,V_LIGHT);
      MyMessage var1valve(0,V_VAR1);
      MyMessage var2valve(0,V_VAR2);
      //
      void setup() 
      { 
       Serial.begin(115200);
       delay(5000);
       lcd.init();
       lcd.clear();
       lcd.backlight();
       lcd.createChar(0, clock);
       lcd.createChar(1, raindrop);
       DEBUG_PRINTLN(F("Initialising..."));
       pinMode(latchPin, OUTPUT);
       pinMode(clockPin, OUTPUT);
       pinMode(dataPin, OUTPUT);
       pinMode(ledPin, OUTPUT);
       attachInterrupt(2, PushButton, CHANGE);
       digitalWrite (ledPin, HIGH);
       // 
       //check for saved date in EEPROM
       DEBUG_PRINTLN(F("Checking EEPROM for stored date:"));
       if (gw.loadState(0) == 0xFF); // EEPROM flag
       {
      	DEBUG_PRINTLN(F("Retreiving last run time from EEPROM..."));
      	for (int i = 0; i < 4 ; i++)
      	{
           lastTimeRun = lastTimeRun << 8;
           lastTimeRun = lastTimeRun | gw.loadState(i+1); // assemble 4 bytes into an ussigned long epoch timestamp
      	}
       }
       gw.begin(getVariables, RADIO_ID, false); // Change 'false' to 'true' to create a Radio repeating node
       gw.sendSketchInfo("MySprinkler", "2.0");
       for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
       {
      	gw.present(i, S_LIGHT);
       }
       DEBUG_PRINTLN(F("Sensor Presentation Complete"));
       //
       DEBUG_PRINTLN(F("Turning All Valves Off..."));
       updateRelays(ALL_VALVES_OFF);
       digitalWrite (ledPin, LOW);
       DEBUG_PRINTLN(F("Ready..."));
       //
       lcd.setCursor(0, 0);
       lcd.print(F(" Syncing Time  "));
       lcd.setCursor(15, 0);
       lcd.write(0);  //lcd.print(0, BYTE);
       lcd.setCursor(0, 1);
       int clockCounter = 0;
       while(timeStatus() == timeNotSet && clockCounter < 21)
       {
      	gw.process();
      	gw.requestTime(receiveTime);
      	DEBUG_PRINTLN(F("Requesting time from Gateway:"));
      	delay(1000);
      	lcd.print(".");
      	DEBUG_PRINT(F("."));
      	clockCounter++;
      	if (clockCounter > 16)
      	{
           DEBUG_PRINTLN(F("Failed initial clock synchronization!"));
           lcd.clear();
           lcd.print(F("  Failed Clock  "));
           lcd.setCursor(0,1);
           lcd.print(F(" Syncronization "));
           delay(2000);
           break;
      	}
       }
       //
       lcd.clear();
      }
      //
      void loop()
      {
       gw.process();
       updateClock();
       updateDisplay();
       goGetValveTimes();
       //
       if (buttonPushed)
       {
      	DEBUG_PRINTLN(F("Button Pressed"));
      	if (state != RUN_ALL_ZONES);
      	{
           state = RUN_ALL_ZONES;
           valveNumber = 1;
           gw.send(msg1valve.setSensor(0).set(true), false);
           startMillis = millis();
           for (byte i = 0; i < 5; i++)  // flash lcd backlight on button press
           {
      		lcd.noBacklight(); 
      		delay(25);
      		lcd.backlight();
           }
           delay(50);
           fastClear();
           lcd.setCursor(0,0);
           lcd.print(F("*AllZone Active*"));
           lcd.setCursor(0,0);
           lcd.print(F(" Cycling  Zones "));
           delay(1000);
           DEBUG_PRINT(F("State = "));
           DEBUG_PRINTLN(state);
      	}
      	buttonPushed = false;
       }
       if (state == STAND_BY_ALL_OFF) 
       {
      	slowToggleLED (); 
      	if (state != lastState)
      	{
           updateRelays(ALL_VALVES_OFF);
           DEBUG_PRINTLN(F("State Changed... all Zones off"));
           for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
           {
      		delay(50);
      		gw.send(msg1valve.setSensor(i).set(false), false);
           }
           lastValve = -1;
      	}
       }
       //
       else if (state == RUN_ALL_ZONES)
       { 
      	if (lastValve != valveNumber)
      	{
           for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
           {
      		if (i == 0 || i == valveNumber)
      		{
      	     gw.send(msg1valve.setSensor(i).set(true), false);
      		}
      		else
      		{
      	     gw.send(msg1valve.setSensor(i).set(false), false);
      		}
           }
      	}
      	lastValve = valveNumber;
      	fastToggleLed();
      	if (state != lastState)
      	{
           valveNumber = 1;
           updateRelays(ALL_VALVES_OFF);
           DEBUG_PRINTLN(F("State Changed, Running All Zones..."));
      	}
      	unsigned long nowMillis = millis();
      	if (nowMillis - startMillis < VALVE_RESET_TIME)
      	{
           updateRelays(ALL_VALVES_OFF);
      	}
      	else if (nowMillis - startMillis < (allZoneTime[valveNumber] * 60000UL))
      	{
           updateRelays(BITSHIFT_VALVE_NUMBER);
      	}
      	else
      	{
           DEBUG_PRINTLN(F("Changing Valves..."));
           updateRelays(ALL_VALVES_OFF);
           startMillis = millis();
           valveNumber++;
           if (valveNumber > NUMBER_OF_VALVES) 
           {
      		state = CYCLE_COMPLETE;
      		startMillis = millis();
      		lastValve = -1;
      		lastTimeRun = now();
      		saveDateToEEPROM(lastTimeRun);
      		for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
      		{
      	     gw.send(msg1valve.setSensor(i).set(false), false);
      		}
      		DEBUG_PRINT(F("State = ")); 
      		DEBUG_PRINTLN(state);
           }
      	}
       }
       //
       else if (state == RUN_SINGLE_ZONE)
       {
      	fastToggleLed();
      	if (state != lastState)
      	{
           for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
           {
      		if (i == 0 || i == valveNumber)
      		{
      	     gw.send(msg1valve.setSensor(i).set(true), false);
      		}
      		else
      		{
      	     gw.send(msg1valve.setSensor(i).set(false), false);
      		}
           }
           DEBUG_PRINTLN(F("State Changed, Single Zone Running..."));
           DEBUG_PRINT(F("Zone: "));
           DEBUG_PRINTLN(valveNumber);
      	}
      	unsigned long nowMillis = millis();
      	if (nowMillis - startMillis < VALVE_RESET_TIME)
      	{
           updateRelays(ALL_VALVES_OFF);
      	}
      	else if (nowMillis - startMillis < (valveSoloTime [valveNumber] * 60000UL))
      	{
           updateRelays(BITSHIFT_VALVE_NUMBER);
      	}
      	else
      	{
           updateRelays(ALL_VALVES_OFF);
           for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
           {
      		gw.send(msg1valve.setSensor(i).set(false), false);
           }
           state = CYCLE_COMPLETE;
           startMillis = millis();
           DEBUG_PRINT(F("State = ")); 
           DEBUG_PRINTLN(state);
      	}
      	lastTimeRun = now();
       }
       else if (state == CYCLE_COMPLETE)
       {
      	if (millis() - startMillis < 30000UL)
      	{
           fastToggleLed();
      	}
      	else
      	{
           state = STAND_BY_ALL_OFF;
      	}
       }
       lastState = state;
      }
      //
      void updateRelays(int value)
      {
       digitalWrite(latchPin, LOW);
       shiftOut(dataPin, clockPin, MSBFIRST, highByte(value)); 
       shiftOut(dataPin, clockPin, MSBFIRST, lowByte(value));
       digitalWrite(latchPin, HIGH);
      }
      //
      void PushButton() //interrupt with debounce
      { 
       static unsigned long last_interrupt_time = 0;
       unsigned long interrupt_time = millis();
       if (interrupt_time - last_interrupt_time > 200)
       {
      	buttonPushed = true;
       }
       last_interrupt_time = interrupt_time;
      }
      //
      void fastToggleLed()
      {
       static unsigned long fastLedTimer;
       if (millis() - fastLedTimer >= 100UL)
       {
      	digitalWrite(ledPin, !digitalRead(ledPin));
      	fastLedTimer = millis (); 
       }   
      }
      //
      void slowToggleLED ()
      {
       static unsigned long slowLedTimer;
       if (millis() - slowLedTimer >= 1250UL)
       {
      	digitalWrite(ledPin, !digitalRead(ledPin));
      	slowLedTimer = millis (); 
       } 
      }
      //
      void getVariables(const MyMessage &message)
      {
       boolean zoneTimeUpdate = false;
       if (message.isAck()) 
       {
      	DEBUG_PRINTLN(F("This is an ack from gateway"));
       }
       for (byte i = 0; i<= NUMBER_OF_VALVES; i++)
       {
      	if (message.sensor == i)
      	{
           if (message.type == V_LIGHT)
           {
      		int switchState = atoi(message.data);
      		if (switchState == 0)
      		{
      	     state = STAND_BY_ALL_OFF;
      	     DEBUG_PRINTLN(F("Recieved Instruction to Cancel..."));
      		}
      		else
      		{
      	     if (i == 0)
      	     {
      			state = RUN_ALL_ZONES;
      			valveNumber = 1;
      			DEBUG_PRINTLN(F("Recieved Instruction to Run All Zones..."));
      	     }
      	     else 
      	     {
      			state = RUN_SINGLE_ZONE;
      			valveNumber = i;
      			DEBUG_PRINT(F("Recieved Instruction to Activate Zone: "));
      			DEBUG_PRINTLN(i);
      	     }
      		}
      		startMillis = millis();
           }
           else if (message.type == V_VAR1)
           {
      		int variable1 = atoi(message.data);// RUN_ALL_ZONES time
      		DEBUG_PRINT(F("Recieved variable1 valve:"));
      		DEBUG_PRINT(i);
      		DEBUG_PRINT(F(" = "));
      		DEBUG_PRINTLN(variable1);
      		if (variable1 != allZoneTime[i])
      		{
      	     allZoneTime[i] = variable1;
           
      	     zoneTimeUpdate = true;
      		}
           }
           else if (message.type == V_VAR2)
           {
      		int variable2 = atoi(message.data);// RUN_SINGLE_ZONE time
      		DEBUG_PRINT(F("Recieved variable2 valve:"));
      		DEBUG_PRINT(i);
      		DEBUG_PRINT(F(" = "));
      		DEBUG_PRINTLN(variable2);
      		if (variable2 != valveSoloTime[i])
      		{
      	     valveSoloTime[i] = variable2;
      	     zoneTimeUpdate = true;
      		}
           }
      	}
       }
       if (zoneTimeUpdate)
       {
      	//
      	DEBUG_PRINTLN(F("New Zone Times Recieved..."));
      	for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
      	{
           if (i != 0)
           {
      		DEBUG_PRINT(F("Zone "));
      		DEBUG_PRINT(i);
      		DEBUG_PRINT(F(" individual time: "));
      		DEBUG_PRINT(valveSoloTime[i]);
      		DEBUG_PRINT(F(" group time: "));
      		DEBUG_PRINTLN(allZoneTime[i]);
      		recentUpdate = true;
           }
      	}
       }
       else
       {
      	recentUpdate = false;
       }
      }
      //
      void updateDisplay()
      {
       static unsigned long lastUpdateTime;
       static boolean displayToggle = false;
       //static byte toggleCounter = 0;
       static SprinklerStates lastDisplayState;
       if (state != lastDisplayState || millis() - lastUpdateTime >= 3000UL)
       {
      	displayToggle = !displayToggle;
      	switch (state){
      	case STAND_BY_ALL_OFF:
           //
           fastClear();
           lcd.setCursor(0,0);
           if (displayToggle)
           {
      		lcd.print(F("  System Ready "));
      		if (clockUpdating)
      		{
      	     lcd.setCursor(15,0);
      	     lcd.write(0);
      		}
      		lcd.setCursor(0,1);
      		lcd.print(hourFormat12() < 10 ? F(" ") : F(""));
      		lcd.print(hourFormat12());
      		lcd.print(minute() < 10 ? F(":0") : F(":"));
      		lcd.print(minute());
      		lcd.print(isAM() ? F("am") : F("pm"));
      		lcd.print(F(" "));
      		lcd.print(month() < 10? F("0") : F(""));
      		lcd.print(month());
      		lcd.print(day() < 10? F("/0") : F("/"));
      		lcd.print(day());
      		lcd.print(F("/"));
      		lcd.print(year()%100);
           }
           else
           {
      		lcd.print(F("  Last Watered "));
      		if (clockUpdating)
      		{
      	     lcd.setCursor(15,0);
      	     lcd.write(0);
      		}
      		lcd.setCursor(0,1);
      		lcd.print(dayOfWeek[weekday(lastTimeRun)]);
      		lcd.setCursor(11,1);
      		lcd.print(month(lastTimeRun) < 10 ? F(" ") : F(""));
      		lcd.print(month(lastTimeRun));
      		lcd.print(day(lastTimeRun) < 10 ? F("/0") : F("/"));
      		lcd.print(day(lastTimeRun));
           }
           break;
      	case RUN_SINGLE_ZONE:
           //
           fastClear();
           lcd.setCursor(0,0);
           if (displayToggle)
           {
      		lcd.print(F("Single Zone Mode"));
      		lcd.setCursor(0,1);
      		lcd.print(F(" Zone:"));
      		if (valveNumber < 10) lcd.print(F("0"));
      		lcd.print(valveNumber);
      		lcd.print(F(" Active"));
           }
           else
           {
      		lcd.print(F(" Time Remaining "));
      		lcd.setCursor(0,1);
      		unsigned long timeRemaining = (valveSoloTime[valveNumber] * 60) - ((millis() - startMillis) / 1000);
      		lcd.print(timeRemaining / 60 < 10 ? "   0" : "   ");
      		lcd.print(timeRemaining / 60);
      		lcd.print("min");
      		lcd.print(timeRemaining % 60 < 10 ? " 0" : " ");
      		lcd.print(timeRemaining % 60);
      		lcd.print("sec  ");
           }
           break;
      	case RUN_ALL_ZONES:
           //
           fastClear();
           lcd.setCursor(0,0);
           if (displayToggle)
           {
      		lcd.print(F(" All-Zone  Mode "));
      		lcd.setCursor(0,1);
      		lcd.print(F(" Zone:"));
      		if (valveNumber < 10) lcd.print(F("0"));
      		lcd.print(valveNumber);
      		lcd.print(F(" Active "));
           }
           else
           {
      		lcd.print(F(" Time Remaining "));
      		lcd.setCursor(0,1);
      		int timeRemaining = (allZoneTime[valveNumber] * 60) - ((millis() - startMillis) / 1000);
      		lcd.print((timeRemaining / 60) < 10 ? "   0" : "   ");
      		lcd.print(timeRemaining / 60);
      		lcd.print("min");
      		lcd.print(timeRemaining % 60 < 10 ? " 0" : " ");
      		lcd.print(timeRemaining % 60);
      		lcd.print("sec  ");
           }
           break;
      	case CYCLE_COMPLETE:
           //
           if (displayToggle)
           {
      		lcd.setCursor(0,0);
      		lcd.print(F(" Watering Cycle "));
      		lcd.setCursor(0,1);
      		lcd.print(F("    Complete    "));
           }
           else
           {
      		int totalTimeRan = 0;
      		for (int i = 1; i < NUMBER_OF_VALVES + 1; i++)
      		{
      	     totalTimeRan += allZoneTime[i];
      		}
      		lcd.setCursor(0,0);
      		lcd.print(F(" Total Time Run "));
      		lcd.setCursor(0,1);
      		lcd.print(totalTimeRan < 10 ? "   0" : "   ");
      		lcd.print(totalTimeRan);
      		lcd.print(" Minutes   ");
           }
      	}
      	lastUpdateTime = millis();
       }
       lastDisplayState = state;
      }
      void receiveTime(time_t newTime)
      {
       DEBUG_PRINTLN(F("Time value received and updated..."));
       int lastSecond = second();
       int lastMinute = minute();
       int lastHour = hour();
       setTime(newTime);
       if (((second() != lastSecond) || (minute() != lastMinute) || (hour() != lastHour)) || showTime)
       {
      	DEBUG_PRINTLN(F("Clock updated...."));
      	DEBUG_PRINT(F("Sensor's time currently set to:"));
      	DEBUG_PRINT(hourFormat12() < 10 ? F(" 0") : F(" "));
      	DEBUG_PRINT(hourFormat12());
      	DEBUG_PRINT(minute() < 10 ? F(":0") : F(":"));
      	DEBUG_PRINT(minute());
      	DEBUG_PRINTLN(isAM() ? F("am") : F("pm"));
      	DEBUG_PRINT(month());
      	DEBUG_PRINT(F("/"));
      	DEBUG_PRINT(day());
      	DEBUG_PRINT(F("/"));
      	DEBUG_PRINTLN(year());
      	DEBUG_PRINTLN(dayOfWeek[weekday()]);
      	showTime = false;
       }
       else 
       {
      	DEBUG_PRINTLN(F("Sensor's time did NOT need adjustment greater than 1 second."));
       }
       clockUpdating = false;
      }
      void fastClear()
      {
       lcd.setCursor(0,0);
       lcd.print(F("                "));
       lcd.setCursor(0,1);
       lcd.print(F("                "));
      }
      //
      void updateClock()
      {
       static unsigned long lastVeraGetTime;
       if (millis() - lastVeraGetTime >= 60000UL) // updates clock time and gets zone times from vera once every 10 minutes
       {
      	DEBUG_PRINTLN(F("Requesting time and valve data from Gateway..."));
      	lcd.setCursor(15,0);
      	lcd.write(0);
      	clockUpdating = true;
      	gw.requestTime(receiveTime);
      	lastVeraGetTime = millis();
       }
      }
      //
      void saveDateToEEPROM(unsigned long theDate)
      {
       DEBUG_PRINTLN(F("Saving Last Run date"));
       if (gw.loadState(0) != 0xFF) 
       {
      	gw.saveState(0,0xFF); // EEPROM flag for last date saved stored in EEPROM (location zero)
       }
       //
       for (int i = 1; i < 5; i++)
       {
      	gw.saveState(5-i, byte(theDate >> 8 * (i-1)));// store epoch datestamp in 4 bytes of EEPROM starting in location one
       }
      }
      //
      void goGetValveTimes()
      {
       static unsigned long valveUpdateTime;
       static byte valveIndex = 1;
       if (millis() - valveUpdateTime >= 300000UL / NUMBER_OF_VALVES)// update each valve once every 5 mins (distributes the traffic)
       {
      	DEBUG_PRINTLN(F("Calling for Valve Times..."));
      	lcd.setCursor(15, 0);
      	lcd.write(1);
      	gw.request(valveIndex, V_VAR1);
      	gw.request(valveIndex, V_VAR2);
      	valveUpdateTime = millis();
      	valveIndex++;
      	if (valveIndex > NUMBER_OF_VALVES+1)
      	{
           valveIndex = 1;
      	}
       }
      }
      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Need help sending custom values

      @hek said:

      @Chaotic

      Send it as hex string in one VAR_x

      E.g. FFcc22

      If you can pass them into MySensors as @hek suggested, you can decode them like this:

      typedef struct {
        byte red;
        byte green;
        byte blue;
      }RGB;
      
      RGB decode(unsigned long value);  // fixed this!!
      
      char * myCstring = "FF00FF";  //<< you recieved this via mySensors radio!!!  (RRGGBB)
      
      void setup()
      {
        Serial.begin(9600);
        Serial.println("STARTING...");
        unsigned long myLong = strtol(myCstring,NULL,16); //<< the magic is here in the strtol() function!!!
        Serial.println(myLong, BIN);
        Serial.println(myLong,HEX);
        RGB newLedColor = decode(myLong);
        char buffer[30] = "\0";
        sprintf(buffer, "Red = %03d, Green = %03d, Blue = %03d", newLedColor.red, newLedColor.green, newLedColor.blue);//<<< you want these to set your leds!!!
        Serial.println(buffer);
      }
      
      void loop()
      {
      
      }
      
      RGB decode(unsigned long value)
      {
        RGB color;
        color.blue = lowByte(value);
        color.green = lowByte(value >> 8);
        color.red = lowByte(value >> 16);
        return color;
      }
      
      posted in Development
      BulldogLowell
      BulldogLowell
    • RE: Boxes and Enclosures

      @samppa

      Embedding them is a terrific idea. I have been focused on making them small and putting in strategic places.

      I photo 1.JPG photo 2.JPG photo 3.JPG photo 4.JPG photo 5.JPG

      posted in Enclosures / 3D Printing
      BulldogLowell
      BulldogLowell
    • RE: Vera Helper with Arduino

      @ServiceXp

      So I've updated this for a four line display and gave it the ability to send a message to the device from its own website:

      Screen Shot 2014-12-21 at 8.01.06 AM.png

      the Display shows lots of data points from Vera:

      Video Here

      Anyone interested I'm making one... I'll post the code... or show some more video.

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Distance+Humidity, Vera Registration Issue

      @NotYetRated

      You are presenting two sensors as child number 0

      gw.sendSensorPresentation(CHILD_ID_HUM, S_HUM);

      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Gateway with LCD

      @marceltrapman said:

      What I need is a couple of pointers/suggestions what I would need to do to intercept status messages and relay them to the display.

      cool idea

      Interesting that someone else recently brought this up to me...

      What about starting with echoing the serial output of the gateway to your LCD display? Try to get the MySensors message to display.

      Flashing the LCD's LED backlight could indicate trouble....
      Displaying in text that it is in pairing mode...

      It's a nice project to pursue.

      posted in Development
      BulldogLowell
      BulldogLowell
    • RE: Vera Helper with Arduino

      @dayve218

      well, having some free time I've expanded it quite a bit, and I am not 100% sure it will run on an UNO... It works on a mega. I added a bunch of stuff:

      • tells you how many unread emails you have using a BASH script on a server to send updates every so often (I'll post that too).

      • control the brightness of the LCD with a dimmer.

      • uses Vera dimmer and switch to control relay and brightness (uses standard xml files to upload to Vera)

      • more weather

      I don't have a display demo right now, been testing in my Serial Monitor

      I can help you with any of it:

      /*
       Vera Auxilliary Controller a.k.a. Arduino/Vera Ethernet Device
       
       December, 2014
       
       Version .91 
       
       This sketch features the following:
       
       * With the device's own website it allows you to send the device a message along with a visual or sound indicator
       * Control LCD backlight
       * Control led (relay) 
       * Control your alarm state or lights or execute scenes...
       * Control attached Relay
       * Processes and displays "Vital" data from Vera like the Weather, Alarm State
       * Reliably runs on an UNO but very expandable if you want to use a Mega instead
       * Displays Indoor Temp and Humidity using DHT sensor
       
       INSTRUCTIONS:
       
       * There are multiple debug serial prints that can be monitored to assure that it is operating properly.
       * https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads for the I2C library, or use yours
       * Add virtual switches to your Vera by following instructions here:http://faq.mios.com/content/2/40/en/how-to-create-virtual-device.html
       * and create a scene and add luup (lua) code like this:
         local state = luup.variable_get("urn:upnp-org:serviceId:SwitchPower1", "Status", 3)
         luup.inet.wget("192.168.1.50//?ledState=“..state..”&”)
       
       copyright (c) by Jim Brower (BulldogLowell@gmail.com)
       */
      #include <Wire.h>
      #include <EEPROM.h>
      #include <SPI.h>   
      //#include <SD.h>  // you will need a MEGA for this, if you want to really expand the web UI
      #include <Ethernet.h>
      #include <EthernetUdp.h>
      #include <utility/w5100.h>
      #include <Time.h>
      #include <Timezone.h>
      #include <LiquidCrystal_I2C.h>
      #include "DHT.h"
      //
      //#define DEBUG_ON   // comment out getNTPtime(); in loop to get this to fit on an UNO for debug, otherwise debug on a MEGA
      //#define USE_DHCP   // uncomment here for DHCP but uses more than 3kBytes, so warm up your MEGA it is too big for your UNO
      #define DIMMER_PIN 6                // available PWM capable pin
      #define DHT_SENSOR_PIN 7            // Thermometer-Hygrometer
      #define RELAY_PIN 13//8                 // Relay control output
      #define DISPLAY_UPDATE_TIME 3000UL  // 3.5 seconds seems OK
      #define NUMBER_OF_MESSAGE_TYPES 17  // Kinds of transmissions from Vera/Internet
      #define TONE_PIN 6                  // If you want Audio alerts
      #define NUMBER_OF_ACTION_BUTTONS 5  // your device can control scenes, devices, etc Vera-side
      //
      #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
      //
      typedef enum{
        LCD_WIND, LCD_TIME, LCD_ALARM_STATUS, LCD_GARAGE, LCD_GUEST_GARAGE, LCD_IP_ADDRESS, LCD_TEMPERATURE, LCD_AC_SETPOINT, LCD_OUTSIDE_TEMP, LCD_WEATHER, LCD_FORECAST, LCD_MESSAGE, LCD_HI_LOW, LCD_EMAIL} 
      lcdState;
      //
      lcdState state = LCD_TIME;  // display start on Time
      //
      //File webFile;
      //
      char *messagePtr;
      const char *messageType[NUMBER_OF_MESSAGE_TYPES] = {
        "?ledStatus=", "?alarmState=", "?garageState=", "?guestGarageState=", "?weatherCondition=", "?outsideTemp=", "?outsideHumid=", "?airconSetpoint=", "?weatherForecast=", "?messageData=", "?todayHigh=", "?todayLow=", "?windSpeed=", "?windDirection=", "?relayState=", "?brightLevel=", "?emailCount="};
      //
      boolean messageFlag = false;
      unsigned long timerStart;
      unsigned long timerDuration;
      int signal = 0;
      //
      boolean lightOn;
      boolean relayState;
      boolean garageOpen = false;
      boolean guestGarageOpen = false;
      char customMessage [30];
      char weatherCondition[30] = {
        "  Not Yet Reported"};
      char weatherForecast[30] = {
        "  Not Yet Reported"};  
      int outdoorTemp = -99;
      int outdoorHumid = -99;
      int todayHigh = -99;
      int todayLow = -99;
      int airconSetpoint = 78;
      int alarmArmed = -1;
      int windSpeed = 99;
      char windDirection[6] = {"ZZZ"};
      char myCharBuffer[30];
      byte brightLevel = 0;
      byte oldBrightLevel;
      int emailCount = -1;
      //
      byte buttonPin[NUMBER_OF_ACTION_BUTTONS] = {
        4, 5, A0, A1, A2};
      byte lastButtonState[4];
      //
      DHT dht;
      //
      LiquidCrystal_I2C lcd(0x27, 20,4);
      uint8_t clock[8] = {
        0x0,0xe,0x15,0x17,0x11,0xe,0x0}; // I'm-fetching-time-indicator for LCD display
      //
      EthernetUDP Udp;
      EthernetServer server(80);
      uint8_t mac[] = { 
        0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE };
      IPAddress ip(192,168,1,50);
      //
      unsigned int localPort = 8888;
      IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov NTP server  // IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov NTP server  // IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov NTP server
      const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
      byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 
      byte socketStat[MAX_SOCK_NUM];
      //
      char myString[100];
      byte locator = 0;
      EthernetClient client;
      const byte veraServer[] = { 
        192,168,1,59 };
      //
      const char *dayOfWeek[] = { 
        "NULL","       Sunday","       Monday", "      Tuesday", "     Wednesday", "      Thursday", "       Friday", "      Saturday"};
      const char *monthString[] = { 
        "NULL", "January", "February", "March", "April", "May", "June", "July", "August","September", "October", "November", "December"};
      // 
      TimeChangeRule usEDT = {
        "EDT", Second, Sun, Mar, 2, -240};  //Eastern Daylight Time = UTC - 4 hours
      TimeChangeRule usEST = {
        "EST", First, Sun, Nov, 2, -300};   //Eastern Standard Time = UTC - 5 hours
      Timezone usET(usEDT, usEST);
      TimeChangeRule *tcr;
      //
      void setup() 
      {
        SERIAL_START(9600);
        lightOn = EEPROM.read(0);
        relayState = EEPROM.read(1);
        pinMode(RELAY_PIN, OUTPUT);
        pinMode(DIMMER_PIN, OUTPUT);
        digitalWrite(RELAY_PIN, relayState? HIGH : LOW);
        //
        for (byte i = 0; i < 4; i++)
        {
          pinMode(buttonPin[i], INPUT_PULLUP);
        }
        //
        dht.setup(DHT_SENSOR_PIN);
        //
      #ifdef USE_DHCP  
         DEBUG_PRINTLN(F("configuring e-net"));
         if (Ethernet.begin(mac) == 0) // start Ethernet and UDP
         {
         DEBUG_PRINTLN(F("Fail Ethernet DHCP")); 
         while(true){
         }
         }
      #else
        Ethernet.begin(mac, ip);
      #endif
        //
        server.begin();
        Udp.begin(localPort);
        //
        lcd.init();
        lcd.clear();
        lightOn? lcd.backlight() : lcd.noBacklight();
        lcd.createChar(0, clock);
      }
      //
      void loop()
      {
        updateLCD();
        getNTPtime();
        webControl();
        messageTimer();
        updateBriteness();
        for (byte i = 0; i < NUMBER_OF_ACTION_BUTTONS; i++)
        {
          byte buttonState = digitalRead(buttonPin[i]);
          if (buttonState == LOW && lastButtonState[i] == HIGH)
          {
            buttonPress(i);
          }
          lastButtonState[i] = buttonState;
        }
      }
      //
      void updateLCD()
      {
        static unsigned long lastDisplayChangeTime;
        if (millis() - lastDisplayChangeTime >= DISPLAY_UPDATE_TIME)
        {
          switch (state) 
          {
          case LCD_TIME:
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            DEBUG_PRINT(F("Time: "));
            DEBUG_PRINT(hourFormat12());
            DEBUG_PRINT(F(":"));
            DEBUG_PRINT(minute());
            DEBUG_PRINTLN(isAM() ? "a" : "p");
            DEBUG_PRINTLN(dayOfWeek[weekday()]);
            lcd.print(dayOfWeek[weekday()]);
            displayStatus();
            if (emailCount != -1)
              state = LCD_EMAIL;
            else
              state = LCD_ALARM_STATUS;
            break;
            //
          case LCD_EMAIL:
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("    Email Count"));
            DEBUG_PRINT(F("emails: "));
            lcd.setCursor(0,2);
            lcd.print(F("         "));
            lcd.print(emailCount);
            DEBUG_PRINTLN(emailCount);
            displayStatus();
            state = LCD_ALARM_STATUS;
            break;
            //
          case LCD_ALARM_STATUS:
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("    Alarm Status"));
            DEBUG_PRINT(F("Alm: "));
            lcd.setCursor(0,2);
            if (alarmArmed == 0)
            {
              lcd.print(F("     Not  Armed"));
            }
            else if (alarmArmed == 1)
            {
              lcd.print(F("        Armed"));
            }
            else
            {
              lcd.print(F("  Not Yet Reported"));
            }
            DEBUG_PRINTLN(alarmArmed ? F("Yes") : F("NO"));
            displayStatus();
            state = LCD_GARAGE;
            break;
            //
          case LCD_GARAGE:
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("    Main  Garage"));
            DEBUG_PRINT(F("Garage: "));
            lcd.setCursor(0,2);
            if (garageOpen == 0)
            {
              lcd.print(F("       Closed"));
            }
            else if (garageOpen == 1)
            {
              lcd.print(F("        Open"));
            }
            else
            {
              lcd.print(F("  Not Yet Reported"));
            }
            DEBUG_PRINTLN(garageOpen ? F("OPEN") : F("CLOSE"));
            displayStatus();
            state = LCD_GUEST_GARAGE;
            break;
            //
          case LCD_GUEST_GARAGE:
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("    Guest Garage"));
            DEBUG_PRINT(F("Guest: "));
            lcd.setCursor(0,2);
            if (guestGarageOpen == 0)
            {
              lcd.print(F("       Closed"));
            }
            else if (guestGarageOpen == 1)
            {
              lcd.print(F("        Open"));
            }
            else
            {
              lcd.print(F("  Not Yet Reported"));
            }
            DEBUG_PRINTLN(guestGarageOpen ? F("OPEN") : F("CLOSE"));
            displayStatus();
            state = LCD_TEMPERATURE;
            break;
            //
          case LCD_TEMPERATURE:
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("  Temperture:"));
            DEBUG_PRINT(F("Inside Temp:"));
            lcd.print(dht.toFahrenheit(dht.getTemperature()), 0);
            lcd.print(char(223));
            lcd.print(F("F"));
            lcd.setCursor(0,2);
            lcd.print(F("    Humidity:"));
            lcd.print(dht.getHumidity(), 0);
            DEBUG_PRINT(F("Humidity:"));
            DEBUG_PRINTLN(dht.getHumidity());
            lcd.print(F("%"));
            displayStatus();
            state = LCD_AC_SETPOINT;
            break;
          case LCD_AC_SETPOINT:
            //
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("    A/C Setpoint"));
            lcd.setCursor(0,2);
            lcd.print(F("        "));
            lcd.print(airconSetpoint);
            lcd.print(char(223));
            lcd.print(F("F"));
            DEBUG_PRINT(F("A/C setpoint:"));
            DEBUG_PRINTLN(airconSetpoint);
            displayStatus();
            state = LCD_OUTSIDE_TEMP;
            break;
            //
          case LCD_OUTSIDE_TEMP:
            //
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F(" Outdoor Temp:"));
            DEBUG_PRINT(F("Ext. Temp:"));
            lcd.print(outdoorTemp);
            lcd.print(char(223));
            lcd.print(F("F"));
            DEBUG_PRINTLN(outdoorTemp);
            lcd.setCursor(0,2);
            lcd.print(F("     Humidity:"));
            DEBUG_PRINT(F("Humidity:"));
            lcd.print (outdoorHumid);
            lcd.print(F("%"));
            DEBUG_PRINTLN(outdoorHumid);
            displayStatus();
            state = LCD_WEATHER;
            break;
            //
          case LCD_WEATHER:
            //
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("  Today's  Weather"));
            DEBUG_PRINT(F("Weather: "));
            lcd.setCursor(0,2);
            lcd.print(weatherCondition);
            DEBUG_PRINTLN(weatherCondition);
            displayStatus();
            state = LCD_HI_LOW;
            break;
            //
          case LCD_HI_LOW:
            //
            fastClearLCD();
            DEBUG_PRINTLN(F("Weather Hi/Lows"));
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("Today's High: "));
            lcd.print(todayHigh);
            lcd.print(char(223));
            lcd.print(F("F"));
            lcd.setCursor(0,2);
            lcd.print(F("         Low: "));
            lcd.print (todayLow);
            lcd.print(char(223));
            lcd.print(F("F"));
            displayStatus();
            if (windSpeed > 2)
            {
              state = LCD_WIND;
            }
            else
            {
              state = LCD_FORECAST;
              DEBUG_PRINTLN(F("wind too light to report..."));
            }
            break;
            // 
          case LCD_WIND:
            //
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("   Winds  Gusting"));
            DEBUG_PRINT(F("Winds: "));
            DEBUG_PRINTLN(windSpeed);
            lcd.print(F(" "));
            lcd.setCursor(1,2);
            if (windSpeed < 10) lcd.print(F(" "));
            lcd.print(windSpeed);
            lcd.print(F("mph from the "));
            lcd.print(windDirection);
            displayStatus();
            state = LCD_FORECAST;
            break; 
          case LCD_FORECAST:
            //
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("      Tomorrow"));
            DEBUG_PRINT(F("Forecast: "));
            lcd.setCursor(0,2);
            lcd.print(weatherForecast);
            DEBUG_PRINTLN(weatherForecast);
            displayStatus();
            if (messageFlag)
              state = LCD_MESSAGE;
            else 
              state = LCD_IP_ADDRESS;
            break;
            //  
          case LCD_MESSAGE:
            //
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            if (signal == 2 && lightOn == 1)
            {
              for (int i = 0; i < 20; i++)
              {
                lcd.noBacklight();
                delay(50);
                lcd.backlight();
                delay(50);
              }
            }
            else if (signal == 1 && lightOn == 1)
            {
              beepTone();
            }
            if (signal != 0) 
            {
              lastDisplayChangeTime - millis();  // give the message back the time lost in the flash/blinking
            }
            lcd.setCursor(0,1);
            lcd.print(F("  *** Message ***"));
            DEBUG_PRINT(F("Message: "));
            lcd.setCursor(0,2);
            lcd.print(customMessage);
            DEBUG_PRINTLN(customMessage);
            displayStatus();
            state = LCD_IP_ADDRESS;
            break;
            //
          case LCD_IP_ADDRESS:
            //
            fastClearLCD();
            lcd.setCursor(0,0);
            timeDate();
            lcd.setCursor(0,1);
            lcd.print(F("     IP Address"));
            DEBUG_PRINT(F("IP Address: "));
            lcd.setCursor(0,2);
            lcd.print("    ");
            for (byte thisByte = 0; thisByte < 4; thisByte++) // print the value of each byte of the IP address:
            {
              lcd.print(Ethernet.localIP()[thisByte], DEC);
              if (thisByte < 3) lcd.print(".");
      #ifdef DEBUG_ON
              Serial.print(Ethernet.localIP()[thisByte], DEC);
      #endif
              if (thisByte < 3) DEBUG_PRINT(".");
              else DEBUG_PRINTLN(F(" "));
            }
            lcd.print(F(" "));
            displayStatus();
            state = LCD_TIME;
            break;
            //      
          }
          lastDisplayChangeTime = millis();
          DEBUG_PRINT(F("free ram:"));
          DEBUG_PRINTLN(freeRam());
        } 
      }
      //
      void sendNTPpacket(IPAddress& address) // Send an NTP request to the time server at the given address 
      {
        memset(packetBuffer, 0, NTP_PACKET_SIZE); 
        packetBuffer[0]  = 0b11100011;   // LI, Version, Mode
        packetBuffer[1]  = 0;            // Stratum, or type of clock
        packetBuffer[2]  = 6;            // Polling Interval
        packetBuffer[3]  = 0xEC;         // Peer Clock Precision
        packetBuffer[12] = 49; 
        packetBuffer[13] = 0x4E;
        packetBuffer[14] = 49;
        packetBuffer[15] = 52;		   
        Udp.beginPacket(address, 123);  //NTP requests are to port 123
        Udp.write(packetBuffer,NTP_PACKET_SIZE);
        Udp.endPacket(); 
      }
      //
      void receiveTime(unsigned long newTime)
      {
        DEBUG_PRINT(F("Time value received: "));
        int lastSecond = second();
        int lastMinute = minute();
        int lastHour = hour();
        setTime(newTime);
        if ((second() != lastSecond) || (minute() != lastMinute) || (hour() != lastHour))
        {
          DEBUG_PRINTLN(F("Clock updated...."));
          DEBUG_PRINT(F("Sensor's time currently set to:"));
          DEBUG_PRINT(hourFormat12() < 10? F(" 0") : F(" "));
          DEBUG_PRINT(hourFormat12());
          DEBUG_PRINT(minute() < 10? F(":0") : F(":"));
          DEBUG_PRINT(minute());
          DEBUG_PRINTLN(isAM()? F("am") : F("pm"));
          DEBUG_PRINT(month());
          DEBUG_PRINT(F("/"));
          DEBUG_PRINT(day());
          DEBUG_PRINT(F("/"));
          DEBUG_PRINTLN(year());
          DEBUG_PRINTLN(dayOfWeek[weekday()]);
        }
        lcd.setCursor(19,0);
        lcd.print(F(" "));
      }
      //
      void getNTPtime()
      {
        static unsigned long lastUpdateTime;
        const unsigned long interval = 60000UL;
        if ((millis() - lastUpdateTime >= interval))
        {                                                         
          lcd.setCursor(19,0);
          lcd.write(0);
          sendNTPpacket(timeServer); // send an NTP packet to a time server
          delay(1000);  
          if (Udp.parsePacket()) 
          {  
            Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
            unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
            unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
            unsigned long secsSince1900 = highWord << 16 | lowWord;  
            DEBUG_PRINT(F("Seconds since Jan 1 1900 = "));
            DEBUG_PRINTLN(secsSince1900);               
            DEBUG_PRINT(F("Unix time = "));
            time_t utcEpoch = secsSince1900 - 2208988800UL;  //seventyYears = 2208988800UL
            DEBUG_PRINTLN(utcEpoch);                               
            receiveTime(usET.toLocal(utcEpoch, &tcr) + 2);   //about 2 seconds to call for time
            lastUpdateTime += interval;
          }
        }
      }
      //
      void webControl()
      {
        EthernetClient client = server.available();
        if (client) 
        {
          while (client.connected()) 
          {
            if (client.available()) 
            {
              char c = client.read();
              if (locator < 100)
              {
                myString[locator] = c;
                locator++;
                myString[locator] = '\0'; 
              }
              if (c == '\n') //if HTTP request has ended
              {
                DEBUG_PRINTLN(F("MyString ="));
                DEBUG_PRINTLN(myString);
                // Send a full website with controls functions and handles message input...
                client.println(F(
                  "<html>"
                  "<head>"
                  "<title>Vera Helper</title>"
                  "</head>"
                  "<body>"
                  "<div align=\"center\">"
                  "<H2>Arduino Auxilliary Controller</H2>"
                  "<H3>for Vera</H3>"
                  "<hr>"
                  "<H2><font color=\"green\">Comfort Settings</H2>"
                  "<H4><font color=\"blue\">Temperature</font>"
                  "<font color=\"red\">"));
                client.println(dht.toFahrenheit(dht.getTemperature()), 1);
                client.println(F("</font>"
                  "<font color=\"blue\">F</font>"
                  "<br />"
                  "<H4><font color=\"blue\">Humidity</font>"
                  "<font color=\"red\">"));
                client.println(dht.getHumidity(), 1);
                client.println(F("</font>"
                  "<font color=\"blue\">%</font>"
                  "<H4><font color=\"midnightBlue\">AirConditioning Setpoint</font>"
                  "<font color=\"red\">"));
                client.println(airconSetpoint);
                client.println(F("</font>"
                  "<font color=\"blue\">F</font>"
                  "<br />"
                  "<hr>"
                  "<h2>"
                  "Send Message<br>"
                  "<form>"
                  "<input type=\"text\" size=\"20\" name= \"messageData\" value=\"Enter message here\">"
                  "<select name=\"time\">"
                  "<option value=\"5\">5min</option>"
                  "<option value=\"15\">15min</option>"
                  "<option selected value=\"30\">30min</option>"
                  "<option value=\"60\">60min</option>"
                  "<option value=\"720\">12hrs</option>"
                  "<option value=\"1440\">24hrs</option>"
                  "</select>"
                  "<h4>"
                  "<div align=\"center\">"
                  "<input type=\"radio\" name=\"signal\" value=\"2\"> Flash<br>"
                  "<input type=\"radio\" name=\"signal\" value=\"1\"> Tone <br>"
                  "<input type=\"radio\" name=\"signal\" value=\"0\" checked> None</h4>"
                  "<input type=\"submit\" onclick=\"alert('Message Sent!')\" value=\"Send\"> <input type=\"reset\" value=\"Reset\"></h2>"
                  "</form>"
                  "<hr>"
                  "<a href=\"/?ledStatus=1?\"\">Turn On LED Backlight</a>    "
                  "<a href=\"/?ledStatus=0?\"\">Turn Off LED Backlight</a>"
                  "<br />"
                  "<a href=\"/?relayState=1?\"\">Turn On Outlet</a>    "
                  "<a href=\"/?relayState=0?\"\">Turn Off Outlet</a><br /> "
                  "<br />"
                  "<a href=\"/?airconSetpoint="));
                if (airconSetpoint > 70) client.println(airconSetpoint - 1);
                else client.println(airconSetpoint);
                client.println(F("?\"\">Lower Air Conditioning Setpoint</a><br /> "
                  "<br />"
                  "<div style=\"background-color:LightSlateGray; color:black; margin:20px; padding:20px;\">"
                  "<h3>Jim Brower</h3>"
                  "<h5>&copy; copyright 2014</h5>"
                  "<h5><a href=\"mailto:bulldoglowell@gmail.com?subject=Arduino/Vera Home Controller\">"
                  "Send Email</a><h5>"
                  "</div>"
                  "</body>"
                  "</html>"));
                delay(1);
                client.stop();
                //
                // let's sort out that HTTP request...
                //
                for (byte i = 0; i < NUMBER_OF_MESSAGE_TYPES; i++)
                {
                  messagePtr = strstr(myString, messageType[i]); // we need a global variable here
                  if (messagePtr)
                  {
                    DEBUG_PRINT(F("network msg..."));
                    DEBUG_PRINTLN(messageType[i]);
                    processMessage(i);
                    break;
                  }
                }
                myString[0] = '\0'; //clearing string for next read
                locator = 0;
              }
            }
          }
        }
      }
      //
      #ifdef DEBUG_ON
      int freeRam () 
      {
        extern int __heap_start, *__brkval; 
        int v; 
        return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
      }
      #endif
      //
      void ShowSockStatus()
      {
        for (int i = 0; i < MAX_SOCK_NUM; i++) 
        {
          DEBUG_PRINT(F("Socket#"));
          DEBUG_PRINT(i);
          uint8_t s = W5100.readSnSR(i);
          socketStat[i] = s;
          DEBUG_PRINT(F(":0x"));
      #ifdef DEBUG_ON
          Serial.print(s,16);
      #endif
          DEBUG_PRINT(F(" "));
          DEBUG_PRINT(W5100.readSnPORT(i));
          DEBUG_PRINT(F(" D:"));
          uint8_t dip[4];
          W5100.readSnDIPR(i, dip);
      #ifdef DEBUG_ON
          for (int j=0; j<4; j++) 
          {
            Serial.print(dip[j],10);
            if (j<3) Serial.print(".");
          }
      #endif
          DEBUG_PRINT(F("("));
          DEBUG_PRINT(W5100.readSnDPORT(i));
          DEBUG_PRINT(F(")"));
        }
      }
      //
      void processMessage(byte httpMessage)
      {
        switch (httpMessage)
        {
        case 0: // "?ledStatus="
          //
          lightOn = parseNumber(httpMessage) ? 1 : 0;
          //(myString[messagePtr - myString + strlen(messageType[httpMessage])]) == '0' ? lightOn = 0 : lightOn = 1;
          EEPROM.write(0, lightOn? 0xFF : 0x00);
          if (lightOn) lcd.backlight();
          else lcd.noBacklight();
          DEBUG_PRINT(F("LCD Backlight: "));
          DEBUG_PRINTLN(lightOn ? F("ON") : F("OFF"));
          break;
        case 1: // "?alarmState="
          //
          alarmArmed = parseNumber(httpMessage) ? 1 : 0;
          DEBUG_PRINTLN(alarmArmed ? F("Alarm is ON") : F("Alarm is OFF"));
          break;
        case 2: // "?garageState=" 
          //
          garageOpen = parseNumber(httpMessage) ? 1 : 0;
          DEBUG_PRINTLN(garageOpen ? F("Garage is Open") : F("garage is Closed"));
          break;
        case 3: // "?guestGarageState="
          //
          guestGarageOpen = parseNumber(httpMessage) ? 1 : 0;
          DEBUG_PRINTLN(guestGarageOpen ? F("Guest garage is Open") : F("Guest garage is Closed"));
          break;
        case 4: // "?weatherCondition="  // Parse Weather 
          //
          {
            strncpy(weatherCondition, parseString(httpMessage), sizeof weatherCondition);
            DEBUG_PRINTLN(weatherCondition);
            if (strstr(weatherCondition, "%20"))
            {
              strncpy(weatherCondition, cleanString(weatherCondition, "%20"), sizeof weatherCondition);
              DEBUG_PRINTLN(weatherCondition);
            }
            if (strlen(weatherCondition) < 19) 
            {
              strncpy(weatherCondition, centerString(weatherCondition), sizeof weatherCondition);
            }
          }
          break;
        case 5: // "?outsideTemp="  // Parse Outside Temperature 
          //
          outdoorTemp = parseNumber(httpMessage);
          break;
        case 6: // "?outsideHumid=" // Parse Outside Humidity 
          //
          outdoorHumid = parseNumber(httpMessage);
          break;
        case 7: // "?airconSetpoint="  // Parse A/C Setpoint 
          //
          airconSetpoint = parseNumber(httpMessage);
          break;
        case 8: // "?weatherForecast="  // Parse the weather forecast
          //
          {
            strncpy(weatherForecast, parseString(httpMessage), sizeof weatherForecast);
            DEBUG_PRINTLN(weatherForecast);
            if (strstr(weatherForecast, "+"))
            {
              strncpy(weatherForecast, cleanString(weatherForecast, "+"), sizeof weatherForecast);
              DEBUG_PRINTLN(weatherForecast);
            }
            if (strlen(weatherForecast) < 19) 
            {
              strncpy(weatherForecast, centerString(weatherForecast), sizeof weatherForecast);
            }
          }
          break;
        case 9: //"?messageData="  // Parse display message
          //
          {
            strncpy(customMessage, parseString(httpMessage), sizeof customMessage);
            if (strstr(customMessage, "+"))
            {
              strncpy(customMessage, cleanString(customMessage, "+"), sizeof customMessage);
              DEBUG_PRINTLN(customMessage);
            }
            if (strlen(customMessage) < 19) 
            {
              strncpy(customMessage, centerString(customMessage), sizeof customMessage);
            }
            Serial.println(myString);
            if (strstr(myString, "&time="))
            {
              DEBUG_PRINTLN(F("Got length marker..."));
              char *timePointer = (strstr(myString, "&time="));
              timerDuration = atoi(timePointer + 6);
              timerStart = millis();
              DEBUG_PRINT(F("message length:"));
              DEBUG_PRINTLN(timerDuration);
            }
            else
            {
              DEBUG_PRINTLN(F("No length marker..."));
              timerDuration = 5;
            }
            if (strstr(myString, "&signal="))
            {
              DEBUG_PRINTLN(F("Got signal..."));
              char *signalPointer = (strstr(myString, "&signal="));
              signal = atoi(signalPointer + 8);
              timerStart = millis();
              DEBUG_PRINT(F("Signal ="));
              DEBUG_PRINTLN(signal);
            }
            else
            {
              DEBUG_PRINTLN(F("No signal..."));
              timerDuration = 5;
            }
            timerStart = millis();
            messageFlag = true;
          }
          break;
        case 10: // "?todayHigh="  // Parse Outside High Temperature 
          //
          todayHigh = parseNumber(httpMessage);
          break;
        case 11: // "?todayLow="  // Parse Outside Low Temperature 
          //
          todayLow = parseNumber(httpMessage);
          break;
        case 12: // "?windSpeed="  // Parse Outside Low Temperature 
          //
          windSpeed = parseNumber(httpMessage);
          break;
        case 13: // "?windDirection"  // Parse Outside Low Temperature 
          //
          strncpy(windDirection, parseString(httpMessage), sizeof windDirection);
          break;
          //
        case 14:  //  "?relayState=" // Get the state of the relay
        //
          relayState = parseNumber(httpMessage) ? 1 : 0;
          //(myString[messagePtr - myString + strlen(messageType[httpMessage])]) == '0' ? relayState = 0 : relayState = 1;
          EEPROM.write(1, relayState ? 0xFF : 0x00);
          digitalWrite(RELAY_PIN, relayState ? HIGH : LOW);
          DEBUG_PRINT(F("Relay Status: "));
          DEBUG_PRINTLN(relayState ? F("ON") : F("OFF"));
          break;
          //
        case 15:  //  "?brightLevel=" // Get the brightness of the LED
        //
          brightLevel = parseNumber(httpMessage);
          brightLevel = map(brightLevel, 0, 100, 0, 255);
          DEBUG_PRINT(F("Brite:"));
          DEBUG_PRINTLN(brightLevel);
          break;
          //
        case 16: // "?emailCount"  // Parse email count 
          //
          emailCount = parseNumber(httpMessage);
          break;
        default:
          {
            //Nothing to do here...
          }
        } 
      }
      //
      void buttonPress(int buttonID)
      {
        DEBUG_PRINTLN(F("buttonPress"));
        ShowSockStatus();
        if (client.connect(veraServer, 3480)) //starts client connection, checks for connection
        {  
          DEBUG_PRINTLN(F("connected"));
          switch (buttonID)
          {
            case 0:  // Toggle LED backlight (?ledStatus=)
              //
              client.print(F("GET /data_request?"
                "id=action&output_format=xml"
                "&DeviceNum=111" // <<<<< Set Vera Device Number for LCD backlight
                "&serviceId=urn:upnp-org:"
                "serviceId:SwitchPower1"
                "&action=SetTarget"
                "&newTargetValue="));
              client.println(lightOn ? F("0") : F("1"));
              client.println(F(" HTTP/1.1"
                "Connection: close"));  //close 1.1 persistent connection  
              client.println(); //end of get request
              delay(1);
              client.stop();
              break;
              //
            case 1:  // Toggle Relay (relayState)
              //
              client.print(F("GET /data_request?"
                "id=action&output_format=xml"
                "&DeviceNum=116" // <<<<< Set Vera Device Number for Relay
                "&serviceId=urn:upnp-org:"
                "serviceId:SwitchPower1"
                "&action=SetTarget"
                "&newTargetValue="));
              client.print(relayState ? F("0") : F("1"));
              client.println(F(" HTTP/1.1"
                "Connection: close"));  //close 1.1 persistent connection  
              client.println(); //end of get request
              delay(1);
              client.stop();
              break;
              //
            case 2:  // Toggle Alarm
              //
              client.print(F("GET /data_request?"
                "id=action&output_format=xml"
                "&DeviceNum=112" /* <<<<< Set Vera Device Number for Alarm */
                "&serviceId=urn:upnp-org:"
                "serviceId:SwitchPower1"
                "&action=SetTarget"
                "&newTargetValue="));
              client.print(relayState ? F("0") : F("1"));
              client.println(F(" HTTP/1.1"
                "Connection: close"));  //close 1.1 persistent connection  
              client.println(); //end of get request
              delay(1);
              client.stop();
              break;
              //
            case 3:  // run lights out scene  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
              //
              client.print(F("GET /data_request?"
                "id=action&output_format=xml"
                "&DeviceNum=112" /* <<<<< Set Vera Device Number for Alarm */
                "&serviceId=urn:upnp-org:"
                "serviceId:SwitchPower1"
                "&action=SetTarget"
                "&newTargetValue="));
              client.print(relayState ? F("0") : F("1"));
              client.println(F(" HTTP/1.1"
                "Connection: close"));  //close 1.1 persistent connection  
              client.println(); //end of get request
              delay(1);
              client.stop();
              break;
              //
            case 4:  // Lower Air Conditioning //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
              //
              client.print(F("GET /data_request?"
                "id=action&output_format=xml"
                "&DeviceNum=112" /* <<<<< Set Vera Device Number for A/C Control */
                "&serviceId=urn:upnp-org:"
                "serviceId:SwitchPower1"
                "&action=SetTarget"
                "&newTargetValue="));
              if (airconSetpoint > 72) client.print(airconSetpoint - 1);
              else client.print(airconSetpoint - 1);
              client.println(F(" HTTP/1.1"
                "Connection: close"));  //close 1.1 persistent connection  
              client.println(); //end of get request
              delay(1);
              client.stop();
              break;
              //
            default:
            {
              //nothing to do here...
            }
          }
        }
      }
      //
      char *parseString(byte messageNumber)
      {
        int textLocation = (messagePtr - myString + strlen(messageType[messageNumber]));
        myCharBuffer[0] = '\0';
        for (byte i = 0; i < 30; i++) // we have room to display only 20 chars, but lets parse 30.
        {
          if (myString[textLocation + i] != '&')
          {
            myCharBuffer[i] = myString[textLocation + i];
            DEBUG_PRINTLN(myCharBuffer[i]);
          }
          else
          {
            DEBUG_PRINTLN(F("got & marker"));
            myCharBuffer[i] = '\0';
            break;
          }
        }
        DEBUG_PRINTLN(myCharBuffer);
        return myCharBuffer;
      }
      //
      char *cleanString(char *targetString, char *searchString)
      {
        while(strstr(targetString, searchString))
        {
          char* stringChunk;
          stringChunk = strstr(targetString, searchString);
          targetString[stringChunk - targetString] = '\0';
          strncpy(myCharBuffer, targetString, sizeof myCharBuffer);
          strncat(myCharBuffer, " ", sizeof myCharBuffer);
          strncat(myCharBuffer, stringChunk + strlen(searchString), sizeof myCharBuffer);
          strncpy(targetString, myCharBuffer, sizeof myCharBuffer);
        }
        return myCharBuffer;
      }
      //
      int parseNumber(byte msgNum)
      {
        if (strstr(myString, messageType[msgNum]))
        { 
          char *messagePointer;
          messagePointer = strtok(myString, "=");
          messagePointer = strtok(NULL, "?");
          int extractedNumber = atoi(messagePointer);
          return extractedNumber;
        }
        else
        {
          DEBUG_PRINTLN(F("Fail parseNumber..."));
          return -99;
        }
      }
      char *centerString(char *stringToCenter)
      {
        char centerSpacing[30];
        for (byte i = 0; i < ((20 - strlen(myCharBuffer)) / 2); i++)
        {
          centerSpacing[i] = ' ';
          centerSpacing[i+1] = '\0';
        }
        strncat(centerSpacing, myCharBuffer, sizeof centerSpacing);
        strncpy(myCharBuffer, centerSpacing, sizeof myCharBuffer);
        return myCharBuffer;
      }
      void messageTimer()
      {
        if (millis() - timerStart >= timerDuration * 60000UL)
        {
          messageFlag = false;
          signal = 0;
        }
      }
      //
      void fastClearLCD()
      {
        for (int i = 1; i < 3; i++)
        {
          lcd.setCursor(0,i);
          for (int j = 0; j < 20; j++)
          {
            lcd.print(F(" "));
          }
        }
      }
      //
      void timeDate()
      {
        if (hourFormat12() < 10) lcd.print(F(" "));
        lcd.print(hourFormat12());
        lcd.print(minute() < 10 ? F(":0") : F(":"));
        lcd.print(minute());
        lcd.print(isAM() ? "am" : "pm");
        lcd.setCursor(11,0);
        if (month() < 10) lcd.print(F(" "));
        lcd.print(month());
        lcd.print(day() < 10 ? F("/0") : F("/"));
        lcd.print(day());
        lcd.print(F("/"));
        lcd.print(year());
      }
      //
      void displayStatus()
      {
        lcd.setCursor(0,3);
        lcd.print(F("A/C:"));
        lcd.print(airconSetpoint);  //lcd.print(dht.toFahrenheit(dht.getTemperature()), 1);
        lcd.print(char(223));
        lcd.print(F("F "));
        lcd.setCursor(11,3);
        if (alarmArmed != 1)
        {
          lcd.print(F("Not Armed"));
        }
        else
        {
          lcd.print(F("    Armed"));
        }
      }
      //
      void beepTone()
      {
        /*
        tone(TONE_PIN, 440, 200);
        noTone(TONE_PIN);
        */
      }
      void updateBriteness()
      {
        if (brightLevel != oldBrightLevel)
        {
          analogWrite(DIMMER_PIN, brightLevel);
        }
        oldBrightLevel = brightLevel;
      }
      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: How to get relay state?

      @C.r.a.z.y.

      something like:

      if (message.sensor == CHILD_ID)
      {
        if (message.type == V_VAR2)
        //etc....
      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides

      @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

      you do it here:

      Serial.println(F("APDS-9960 initialization complete"));
      

      but not here:

      Serial.println("Relay status requested from Domoticz");
      

      You shouuld wrap every every every string constant in the F Macro

      posted in Development
      BulldogLowell
      BulldogLowell
    • RE: Rain Guage

      @Francois

      yes is working still, I mentioned how I did the math above...

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: RelayWithButtonActuator - another n00b question

      @gadu

      String totalProjectSuccess = itWorks + wifeIsHappy;
      

      🙂

      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides

      @Ben-Andrewes said in MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides:

      #ifdef SerialPrints
        Serial.println("First boot checks completed");
      

      whoopsie... you missed a few.

      as a side note,

      if you have two identical string constants... the compiler is smart enough to evaluate them as such and store only one and access it accordingly.

      also, the following expression means wherever preprocessor sees an argument wrapped in DEBUG_PRINT(), it should replace it with that argument wrapped in Serial.print():

      #define DEBUG_PRINT(x)   Serial.print(x)
      

      this expression, on the other hand, means wherever preprocessor sees an argument wrapped in DEBUG_PRINT(), it should replace it with nothing:

      #define DEBUG_PRINT(x)
      

      @dbemowsk said in MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides:

      @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

      Sorry for all the questions but I am trying to get my head around compiling vs programming and then how I can use it to make things more efficient...

      No need to be sorry, we all learn, even me. When I first saw that bit of code, I too thought it was genius.

      My understanding of things is that lines that start with a hashtag are dealt with by the compiler and are not compiled into the program, to a point. Here is an example.

      they are actually preprocessor directives, and that happens before compilation.

      posted in Development
      BulldogLowell
      BulldogLowell
    • RE: Rain Guage

      @Francois

      Sure, but I have that running at my 'other' house and it is on older version of MySensors (haven't upgraded there yet 😉

      I'll take a whack at updating if you like... it needs to get done anyway... here it is:

      /*
       Arduino Tipping Bucket Rain Gauge
       
       June 15, 2014
       
       Version 1.00b   
       
       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 24, 48, 72, 96 and 120
       hours.  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. 
       
       This sketch features the following:
       
       * Allows you to set the rain threshold in mm  
       * Allows you to determine the interval window up to 120 hours.
       * Displays the last 24, 48, 72, 96 and 120 hours total rain in Variable1 through Variable5
         of the Rain Sensor device
       * Configuration changes to Sensor device updated every 3 hours.
       * SHould run on any Arduino
       * Will retain Tripped/Not Tripped status and data in a power outage, saving small ammount
         of data to EEPROM (Circular Buffer to maximize life of EEBPROM
       * There is a unique setup requirement necessary in order to properly present the Vera device 
         variables.  The details are outlined in the sketch below.
       * LED status indicator
       
       by BulldogLowell@gmail.com for free public use
       
       */
      #include <SPI.h>
      #include <EEPROM.h>  
      #include <RF24.h>
      #include <Sensor.h>
      //
      #define STATE_LOCATION 513 // stay away from EEPROM used with Sensor.h
      #define EEPROM_BUFFER 514  // location of the EEPROM circular buffer
      #define BUFFER_LENGTH 121
      //
      Sensor gw;
      boolean metric = false;
      //
      int eepromIndex;
      int tipSensorPin = 3;
      int ledPin = 5; //PWM capable required
      unsigned long dataMillis;
      unsigned long serialInterval = 10000UL;
      const unsigned long oneHour = 60000UL;
      unsigned long lastTipTime;
      unsigned long lastBucketInterval;
      unsigned long startMillis;
      int dayBuckets [5] [2] = {
       { V_VAR1, 24 },
       { V_VAR2, 48 },
       { V_VAR3, 72 },
       { V_VAR4, 96 },
       { V_VAR5, 120},
       };
      volatile byte rainBucket [120] ; // 5 days of data
      const unsigned long calibrateFactor = 1UL; //Calibration in milimeters of rain per single tip
      unsigned long rainRate = 0;
      float currentRain = 0;
      boolean wasTipped = false;
      boolean updateVera;
      int rainCount;
      volatile int tipBuffer = 0;
      byte rainWindow = 72;//default rain window in hours
      int rainSensorThreshold = 15;//default rain sensor sensitivity in mm
      byte hourCount = 24;
      byte state;
      byte oldState = -1;
      unsigned long ledPulseTime = 500UL;
      unsigned long pulseStart;
      //
      void setup()  
      { 
        Serial.begin(115200);
        gw.begin();
        //
        pinMode(tipSensorPin, OUTPUT);
        attachInterrupt (1, sensorTipped, CHANGE);
        pinMode(ledPin, OUTPUT);
        digitalWrite(ledPin, HIGH);
        gw.sendSketchInfo("Rain Guage", "1.00b"); 
        gw.sendSensorPresentation(1, S_RAIN);
        gw.sendSensorPresentation(2, S_MOTION);
        Serial.println(F("Sensor Presentation Complete"));
        pinMode(tipSensorPin, INPUT);
        //
        state = EEPROM.read(STATE_LOCATION);
        for (int i = 0; i < BUFFER_LENGTH; i++)
        {
          byte locator = EEPROM.read(EEPROM_BUFFER + i);
          if (locator == 0xFF)
          {
            eepromIndex = EEPROM_BUFFER + i;
            loadRainArray(eepromIndex);
            Serial.println(eepromIndex);
            break;
          }
        }
        //
        gw.sendVariable(2, V_TRIPPED, state);
        dataMillis = millis();
        startMillis = millis();
        lastTipTime = millis() - oneHour;
        // uncomment the following block the first time you include this device.  The delays
        // are necessary in order to create the five Variable buckets in the correct order.
        // Once you create them, you can comment out these lines and re-upload to your arduino
        //------------------------------------------------------
        /*
        for (int j = 0; j < 5; j++)
         {
         delay(2000);
         gw.sendVariable(1, dayBuckets[j] [0], 1);
         }
         delay(2000);
         gw.sendVariable(2,V_VAR1, 1);
         delay(2000);
         gw.sendVariable(2,V_VAR2, 1);
         */
        //------------------------------------------------------  
        rainWindow = atoi(gw.getStatus(2, V_VAR1));
        delay(2000);
        rainSensorThreshold = atoi(gw.getStatus(2, V_VAR2));
        gw.sendVariable(1, V_RAINRATE, 0);
        Serial.print(F("Radio Done"));  
        analogWrite(ledPin, 20);
      }
      //
      void loop()     
      { 
        unsigned long measure = 0; // Check to see if we need to show sensor tripped in this block
        for (int i = 0; i < rainWindow; i++)
        {
          measure = measure + rainBucket [i];
        }
        measure >= rainSensorThreshold ? state = 1: state = 0;
        if (millis() - pulseStart < ledPulseTime)
        {
          analogWrite(ledPin, 255);
        }
        else 
        {
          analogWrite(ledPin, 20);
        }
        if (state != oldState)
        {
          gw.sendVariable(2, V_TRIPPED, state);
          EEPROM.write(STATE_LOCATION, state);
          oldState = state;
        }
        //
        if (millis() - lastTipTime >= oneHour)// timeout for rain rate
        {
          if (rainRate != 0)
          {
            rainRate = 0;
            gw.sendVariable(1, V_RAINRATE, rainRate);
          }
        }
        if (updateVera)
        {
          gw.sendVariable(1, V_RAINRATE, rainRate);
          updateVera = false;
        }
        //
        if ( (millis() - dataMillis) >= serialInterval)//Comment back in this block to enable Serial prints
        {
          for (int i = 24; i <= 120; i=i+24)
          {
            updateSerialData(i);
          }
          dataMillis = millis();
        }
        //
        if (tipBuffer > 0)
        {
          Serial.println(F("Sensor Tipped"));
          rainBucket [0] ++;
          pulseStart = millis();
          if (rainBucket [0] > 253) rainBucket[0] = 253; // odd occurance but prevent overflow
          int dayTotal = 0;
          for (int i = 0; i < 24; i++)
          {
            dayTotal = dayTotal + rainBucket [i];
          }
          gw.sendVariable(1, V_RAIN, dayTotal);
          unsigned long tipDelay = millis() - lastTipTime;
          if (tipDelay <= oneHour) 
          {
            rainRate = ((oneHour) / tipDelay);
            gw.sendVariable(1, V_RAINRATE, rainRate);
            Serial.print(F("RainRate= "));
            Serial.println(rainRate);
          }
          lastTipTime = millis();
          updateVera = true;
          tipBuffer--;
        }
        if ( (millis()- startMillis) >= oneHour)
        {
          Serial.println("one hour elapsed");
          //EEPROM write last value
          EEPROM.write(eepromIndex, rainBucket[0]);
          eepromIndex++;
          if (eepromIndex > EEPROM_BUFFER + BUFFER_LENGTH) eepromIndex = EEPROM_BUFFER;
          Serial.println(eepromIndex);
          EEPROM.write(eepromIndex, 0xFF);
          //
          for (int i = BUFFER_LENGTH - 1; i >= 0; i--)//cascade an hour of values
          {
            rainBucket [i + 1] = rainBucket [i];
          }
          rainBucket[0] = 0;
          gw.sendVariable(1, V_RAIN, tipCounter(24));// send 24hr tips
          startMillis = millis();
          for (int j = 0; j < 5; j++)
          {
            int total = 0;
            for (int i = 0; i < dayBuckets[j][1]; i++)
            {
              total = total + rainBucket [i];
            }
            gw.sendVariable( 1, dayBuckets[j] [0], total);
          }
          hourCount++;
          if (hourCount >=3)//8 times daily update the Sensor variables
          {
            rainWindow = atoi(gw.getStatus(2, V_VAR1));
            if (rainWindow < 6) 
            {
              rainWindow = 6;
              gw.sendVariable( 2, V_VAR1, rainWindow);
            }
            if (rainWindow > 120) 
            {
              rainWindow = 120;
              gw.sendVariable( 2, V_VAR2, rainWindow);
            }
            rainSensorThreshold = atoi(gw.getStatus(2, V_VAR2));
            delay(2000);
            //
            if (rainSensorThreshold < 1) 
            {
              rainSensorThreshold = 1;
              gw.sendVariable( 2, V_VAR2, rainSensorThreshold);
            }
            if (rainSensorThreshold > 1000) 
            {
              rainSensorThreshold = 1000;
              gw.sendVariable( 2, V_VAR2, rainSensorThreshold);
            }
            //
            hourCount = 0;
          }
        }
      }
      //
      void sensorTipped()
      {
        static unsigned long last_interrupt_time = 0;
        unsigned long interrupt_time = millis();
        if (interrupt_time - last_interrupt_time > 200)
        {
          tipBuffer++;
        }
        last_interrupt_time = interrupt_time;
      }
      //
      unsigned long tipCounter(int x)
      {
        int tipCount = 0;
        for ( int i = 0; i < x; i++)
        {
          tipCount = tipCount + rainBucket [i];
        }
        return tipCount;
      }
      //
      void updateSerialData(int x)
      {
        Serial.print(F("Tips last "));
        Serial.print(x);
        Serial.print(F("hours: "));
        int tipCount = 0;
        for (int i = 0; i < x; i++)
        {
          tipCount = tipCount + rainBucket [i];
        }
        Serial.println(tipCount);
      }
      void loadRainArray(int value)
      {
        for (int i = 0; i < BUFFER_LENGTH - 1; i++)
         {
           value--;
           Serial.println(value);
           if (value < EEPROM_BUFFER) 
             {
               value = EEPROM_BUFFER + BUFFER_LENGTH;
             }
           byte rainValue = EEPROM.read(value);
           Serial.println(rainValue);
           if (rainValue < 255)
           {
             rainBucket[i] = rainValue;
           }
           else
           {
             rainBucket [i] = 0;
           }
         }
      }
      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Soil Sensor analog values differ on Uno vs Pro Mini

      @fred97 said:

      i don't understand why is alway inverted ??

      try to use this:

      gw.send(msg.set(constrain(map(soilValue, 1023, 250, 0, 100),0,100)));
      

      it maps 1023 to zero and 250 to 100... that is what you want, yes?

      #include <SPI.h>
      #include <MySensor.h>
      
      #define ANALOG_INPUT_SOIL_SENSOR A0
      #define CHILD_ID 0 // Id of the sensor child
      
      MySensor gw;
      MyMessage msg(CHILD_ID, V_HUM);
      int lastSoilValue = -1;
      
      void setup()
      {
        gw.begin();
      
        // Send the sketch version information to the gateway and Controller
        gw.sendSketchInfo("Soil Moisture Sensor - analog", "1.0");
        // sets the soil sensor analog pin as input
        pinMode(ANALOG_INPUT_SOIL_SENSOR, INPUT);
        // Register all sensors to gw (they will be created as child devices)
        gw.present(CHILD_ID, S_HUM);
      }
      
      void loop()
      {
        // Read analog soil value
        int soilValue = analogRead(ANALOG_INPUT_SOIL_SENSOR);
      
        if (soilValue != lastSoilValue) {
          Serial.println(soilValue);
          gw.send(msg.set(constrain(map(soilValue, 1023, 250, 0, 100),0,100)));
          lastSoilValue = soilValue;
        }
        // Power down the radio and arduino until analog input changes.
        gw.sleep(1000);
      }
      
      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Why text-based communication???

      @4Project said in Why text-based communication???:

      Does that mean all the communication is always destined/sourced to/from the controller?

      I think that is explained in the network topology.

      If you want to send a message node-to-node, you could try to manage it through your gateway or a repeater.

      posted in Development
      BulldogLowell
      BulldogLowell
    • RE: 'MySensoring' a Kidde Smoke Detector. (Completed)

      @ServiceXp

      sure,

      here in arduino land an int is 16 bits, and default is signed

      so for 16 bit ints, the maximum representable value 2^(16 − 1) =32,768 (signed)

      adding the UL tells the compiler "Hey dude, I'm an Unsigned Long!"

      UL's are 32bit... and for 32 bits, well the the maximum representable value 2^32 = 4,294,967,295

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: How to sleep the Arduino but still have it respond to the Vera?

      @twosh said:

      Since my rfid doesn't generate an interrupt (attached to A4 and A5)

      So, your RFID device communicates with I2C, so you may be able to do what you want here.

      You could always try to connect to a pin with an interrupt (D2 or D3) by a jumper from A4 or A5. Use a resistor across the pins to limit how much current the interrupt pin sinks.

      Attach in a similar way the radio to the other interrupt pin.

      so ideally, you are waking your arduino from its slumber on an interrupt from either the RFID scanner or the radio.

      Which RFID device are you using?

      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Weather Station with Scene Activator!!!

      @cleight

      like this?

      link text

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Doorbell hack

      not tested, but I'd look to do it like this... with non-blocking code in case of a second press of the doorbell within the 5second timeout:

      /*
       this is set up for a 4 pin recv unit GND DATA DATA VCC
       plug GND into D2, DATA into D3 and D4, and VCC into D5
      */
      
      #include <MySensor.h>
      #include <SPI.h>
      #include <Bounce2.h>
      #include <RCSwitch.h>
      RCSwitch mySwitch = RCSwitch();
      
      #define CHILD_ID 3
      #define VCC_PIN 5 // source 5V up to 40mA from this pin
      #define GND_PIN 2 // sink up to 40mA on this pin
      #define DATA_PIN 3 // external int 1 on Uno
      
      MySensor gw;
      const int bell = -251;     // This is the value that my 433mhz doorbell sends out when button is pushed
      boolean tripped = false;
      // Change to V_LIGHT if you use S_LIGHT in presentation below
      MyMessage msg(CHILD_ID, V_TRIPPED);
      
      void setup()
      {
        gw.begin();
      
        // Register binary input sensor to gw (they will be created as child devices)
        // You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage.
        // If S_LIGHT is used, remember to update variable type you send in. See "msg" above.
        gw.present(CHILD_ID, S_DOOR);
        // 433mhz Part
        pinMode(DATA_PIN, INPUT);
        // just leave D4 tristated
        pinMode(GND_PIN, OUTPUT);
        digitalWrite(GND_PIN, LOW);
        pinMode(VCC_PIN, OUTPUT);
        digitalWrite(VCC_PIN, HIGH);
        mySwitch.enableReceive(1);  // Receiver on interrupt 1 => that is pin D3
      }
      
      void loop()
      {
        if (mySwitch.available())
        {
          tripped = (mySwitch.getReceivedValue() == bell);
          if (tripped)
          {
            gw.send(msg.set("1"));
            timerStart = millis();
            Serial.println(F("Ding Dong!!"));
          }
          mySwitch.resetAvailable();  //<<<<<<<<<< I think you want this here... I looked at the library on GitHub
        }
        if (tripped && millis() - timerStart > 5000UL)
        {
          gw.send(msg.set("0"));
          Serial.println(F("no activity"));
          tripped = false;
        }
      }
      

      again, I could not test it...

      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Mini Weather Station

      @hek said:

      Might print it in some other color though.. 😉

      Are you kidding, all of the cool birdies would want to live there!

      @jtm312 Very nicely built!!

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Doorbell hack

      @msebbe

      OK, I reviewed the arduino library and it looks like mySwitch.resetAvailable(); belonged higher in the code, so I edited it. You may want to be wary of that.

      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Mini Weather Station
      else if (temperature != lastTemp)
      

      @jtm312

      So I've been working with temperature sensors a while and I worry about comparing floats, and their notorious precision triggering a true here and superfluously affecting the rate of the radio updates. The DHT22 is accurate to about 1 Celsius degree, so it may be unnecessary to transmit a change if there is a change to temperature at any precision of less than 1.

      maybe try something like:

      if (temperature > lastTemp + 0.5 || temperature < lastTemp - 0.5)
      {
        doSomething();
        lastTemp = temperature;
      }
      

      Would reduce transmissions and save battery?

      same argument with your hygrometer and barometric pressure, but different level of precision. Not for the weather calculations, just for the decision to transmit updates, IMO.

      all easy to test with your serial monitor.

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Doorbell hack

      @msebbe

      try inserting:

      unsigned long timerStart;
      

      to the header in the next line following:

      boolean tripped = false;
      
      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Rain Guage

      @hek
      @petewill

      A little progress on the device...

      Screen Shot 2015-05-09 at 11.07.41 PM.png

      Screen Shot 2015-05-09 at 11.17.11 PM.png

      Screen Shot 2015-05-09 at 11.07.28 PM.png

      Screen Shot 2015-05-09 at 11.11.29 PM.png

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Humidity, temperature, motion and bunch of relays...

      @Tomasz-Pazio

      an approach with the relays would be like this bit of (untested) code:

      
      #include <MySigningNone.h>
      #include <MyTransportNRF24.h>
      #include <MyTransportRFM69.h>
      #include <MyHwATMega328.h>
      #include <MySensor.h>
      #include <SPI.h>
      
      #define NUMBER_OF_RELAYS 8
      #define RELAY_ON 1  // GPIO value to write to turn on attached relay
      #define RELAY_OFF 0 // GPIO value to write to turn off attached relay
      
      const int relayPin[NUMBER_OF_RELAYS] = {5,6,7,8,14,15,16,17};  //<<<<<<<pin numbers
      
      MyTransportNRF24 radio(RF24_CE_PIN, RF24_CS_PIN, RF24_PA_LEVEL_GW);  
      
      MyHwATMega328 hw;
      
      MySensor gw(radio, hw);
      
      void setup()  
      {   
        gw.begin(incomingMessage, AUTO, true);
        gw.sendSketchInfo("multiRelay", "1.0");
        for (int i = 0; i < NUMBER_OF_RELAYS; i++) 
        {
          gw.present(i, S_LIGHT);
          pinMode(relayPin[i], OUTPUT);   
          digitalWrite(relayPin[i], LOW);
        }
      }
      
      void loop() 
      {
        gw.process();
      }
      
      void incomingMessage(const MyMessage &message) 
      {
        if (message.type == V_LIGHT) 
        {
           digitalWrite(relayPin[message.sensor], message.getBool()? RELAY_ON : RELAY_OFF);
         } 
      }
      
      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Rain Guage

      @hek
      recessing the electronics into the bottom would be tough in this design. I was taking advantage on the two parts to use the smooth plate of the printer, and then make a gasket or even use silicone. lemme think about it. I could add a couple of deflectors on the bottom plate to glue on around where the weep holes are...

      PS enough room for 2 C sized cells

      bottom view:

      Screen Shot 2015-05-10 at 11.32.50 AM.png

      Screen Shot 2015-05-10 at 11.44.44 AM.png

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: RGB Light, cancel loop?

      @msebbe

      an easy way would be a non-blocking function that breathes the LED... I did this for a recent project:

      void breatheUpdate(const uint8_t * segment, const uint8_t numLeds, const uint32_t increment, const uint8_t step)
      {
        static uint32_t lastTimeChange = 0;
        static uint8_t direction = 1;
        const static uint8_t lowLimit = 50;
        static uint8_t value = lowLimit;
        if(millis() - lastTimeChange > increment)
        {
          value +=(direction * step);
          value = constrain(value, lowLimit, 255);
          if (value <= lowLimit || value >= 255)
          {
            direction = direction * -1;
          }
          for(uint8_t i = 0; i < numLeds; i++)
          {
            myPixels.setPixelColor(segment[i], myPixels.Color(0, 0, value));
          }
          myPixels.show();
          lastTimeChange += increment;
        }
      }
      

      segment here is an array of leds, but you can convert the function to a single neopixel easily.

      loop() would look like this:

      void loop()
      {
        gw.process();
        if (isShow)
        {
          breatheUpdate(mySegment, sizeof(mySegment), breatheRate, 1);
        }
      }
      
      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Pirate Chest with Secret Knock Sensor

      @Sparkman said:

      I think Canada customs is to blame in some instances for my shipments

      Screen Shot 2015-05-19 at 10.41.44 PM.png

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: problem in SoilMoistSensor

      @Reza

      why not post the sketch you are using?

      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Rain Guage

      @petewill

      here is some (untested) mods to update the 5 day rainfall history once per day at midnight. Take a look, let me know what you think (any problems issues). It turns out I was overthinking the problem and it really only needed minor mods...

      /*
       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 24, 48, 72, 96 and 120
       hours.  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.
      
       This sketch features the following:
      
       * Allows you to set the rain threshold in mm
       * Allows you to determine the interval 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 ammount
         of data to EEPROM (Circular Buffer to maximize life of EEPROM)
       * LED status indicator
      
       by @BulldogLowell and @PeteWill for free public use
      
       */
      #include <SPI.h>
      #include <MySensor.h>
      #include <math.h>
      #include <Time.h>
      
      #define NODE_ID 24
      #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 CHILD_ID_RAIN_LOG 3  // Keeps track of accumulated rainfall
      #define CHILD_ID_TRIPPED_INDICATOR 4  // Indicates Tripped when rain detected
      
      #define EEPROM_STATE_LOCATION 0 // location to save state to EEPROM
      #define EEPROM_BUFFER_LOCATION 1  // location of the EEPROM circular buffer
      #define BUFFER_LENGTH 121
      
      #define CALIBRATE_FACTOR 100 // e.g. 5 is .05mm (or 5 hundredths of an inch if imperial) per tip
      
      #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);
      //
      boolean metric = true;
      int eepromIndex;
      int tipSensorPin = 3; // Must be interrupt capable pin
      int ledPin = 5; // PWM capable pin required
      unsigned long dataMillis;
      const unsigned long serialInterval = 10000UL;
      const unsigned long oneHour = 3600000UL;
      unsigned long lastTipTime;
      unsigned long startMillis;
      unsigned int rainBucket [24] ; /* 24 hours = 1 day of data */
      unsigned int rainRate = 0;
      volatile int wasTippedBuffer = 0;
      byte rainWindow = 72;         //default rain window in hours
      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;
      
      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);
        delay(DWELL_TIME);
        gw.present(CHILD_ID_RAIN_LOG, S_RAIN);
        delay(DWELL_TIME);
        gw.present(CHILD_ID_TRIPPED_INDICATOR, S_MOTION);
        delay(DWELL_TIME);
        DEBUG_PRINTLN(F("Sensor Presentation Complete"));
        state = gw.loadState(EEPROM_STATE_LOCATION); //retreive prior state from EEPROM
        DEBUG_PRINT(F("Previous Tripped State (from EEPROM): "));
        DEBUG_PRINTLN(state ? "Tripped" : "Not Tripped");
        //
        gw.send(msgTripped.set(state));
        delay(DWELL_TIME);
        //
        //Sync time with the server, this will be called hourly in order to keep time from creeping with the crystal
        //
        while(timeStatus() == timeNotSet)
        {
          gw.process();
          gw.requestTime(receiveTime);
          Serial.println("getting Time");
          delay(1000); // call once per second
          Serial.print(".");
        }
        //
        //retrieve from EEPROM stored values on a power cycle.
        //
        boolean isDataOnEeprom = false;
        for (int i = 0; i < BUFFER_LENGTH; i++)
        {
          byte locator = gw.loadState(EEPROM_BUFFER_LOCATION + 2 * i); //<<<<<<<<<<<
          if (locator == 0xFF)  // found the EEPROM circular buffer index
          {
            eepromIndex = EEPROM_BUFFER_LOCATION + 2 * i;
            //Now that we have the buffer index let's populate the rainBucket[] with data from eeprom
            loadRainArray(eepromIndex);
            isDataOnEeprom = true;
            DEBUG_PRINT("EEPROM Index ");
            DEBUG_PRINTLN(eepromIndex);
            isDataOnEeprom = true;
            break;
          }
        }
        if (!isDataOnEeprom) // Added for the first time it is run on a new arduino
        {
          eepromIndex = 1;
          gw.saveState(eepromIndex, 0xFF); // store the EEPROM index marker...
          gw.saveState(eepromIndex + 1, 0xFF);
        }
        dataMillis = millis();
        startMillis = millis();
        lastTipTime = millis() - oneHour;
        //
        //Get sensor time window and threshold from controller
        gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR1); 
        delay(DWELL_TIME);
        gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR2); 
        delay(DWELL_TIME);
        DEBUG_PRINTLN(F("Radio Setup Complete!"));
      }
      
      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);
        if (state != oldState)
        {
          gw.send(msgTripped.set(state));
          delay(DWELL_TIME);
          gw.saveState(EEPROM_STATE_LOCATION, state); //New Code
          DEBUG_PRINT(F("New Sensor State... Sensor: "));
          DEBUG_PRINTLN(state? "Tripped" : "Not Tripped");
          oldState = state;
        }
        //
        unsigned long tipDelay = millis() - lastTipTime;
        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]);
          //
          int dayTotal = 0;
          for (int i = 0; i < 24; i++)
          {
            dayTotal = dayTotal + rainBucket [i];
          }
          //
          DEBUG_PRINT(F("dayTotal value: "));
          DEBUG_PRINTLN(dayTotal);
          gw.send(msgRain.set(dayTotal, 1));
          delay(DWELL_TIME);
          wasTippedBuffer--;
          rainRate = ((oneHour) / tipDelay);
          if (rainRate != lastRainRate)
          {
            gw.send(msgRainRate.set(rainRate, 1));
            delay(DWELL_TIME);
            DEBUG_PRINT(F("RainRate= "));
            DEBUG_PRINTLN(rainRate);
            lastRainRate = rainRate;
          }
        }
        if (tipDelay > oneHour)
        {
          rainRate = 0;
          gw.send(msgRainRate.set(rainRate, 1));
        }
        //
        if (millis() - startMillis >= oneHour)
        {
          DEBUG_PRINTLN(F("One hour elapsed."));
          for (int i = BUFFER_LENGTH - 1; i >= 0; i--)//cascade an hour of values back into the array
          {
            rainBucket [i + 1] = rainBucket [i];
          }
          rainBucket[0] = 0;
          gw.send(msgRain.set(rainTotal(24), 1)); // send 24hr tips
          delay(DWELL_TIME);
          gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR1);
          delay(DWELL_TIME);
          gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR2);
          delay(DWELL_TIME);
          gw.saveState(eepromIndex, highByte(rainBucket[0]));
          gw.saveState(eepromIndex + 1, lowByte(rainBucket[0]));
          eepromIndex++;
          if (eepromIndex > EEPROM_BUFFER_LOCATION + BUFFER_LENGTH) eepromIndex = EEPROM_BUFFER_LOCATION;
          DEBUG_PRINT(F("Writing to EEPROM.  Index: "));
          DEBUG_PRINTLN(eepromIndex);
          gw.saveState(eepromIndex, 0xFF);
          gw.saveState(eepromIndex + 1, 0xFF);
          gw.requestTime(receiveTime); // sync the time every hour
          delay(DWELL_TIME);
          startMillis = millis();
        }
        if (hour() == 0 and lastHour == 23)
        {
          transmitRainData();
        }
        lastHour = hour();
      }
      
      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("Tips last "));
        DEBUG_PRINT(x);
        DEBUG_PRINTLN(F(" hours: "));
        int tipCount = 0;
        for (int i = 0; i < x; i++)
        {
          tipCount = tipCount + rainBucket [i];
        }
        DEBUG_PRINTLN(tipCount);
      }
      
      void loadRainArray(int value) // load stored rain array from EEPROM on powerup
      {
        for (int i = 0; i < BUFFER_LENGTH - 1; i++)
        {
          value--;
          DEBUG_PRINT("EEPROM location: ");
          DEBUG_PRINTLN(value);
          if (value < EEPROM_BUFFER_LOCATION)
          {
            value = EEPROM_BUFFER_LOCATION + BUFFER_LENGTH;
          }
          byte rainValueHigh = gw.loadState(value);
          byte rainValueLow = gw.loadState(value + 1);
          int rainValue = (rainValueHigh << 8) & rainValueLow;
          rainBucket[i] = rainValue;
          //
          DEBUG_PRINT(F("rainBucket[ value: "));
          DEBUG_PRINT(i);
          DEBUG_PRINT(F("] value: "));
          DEBUG_PRINTLN(rainBucket[i]);
        }
      }
      
      void transmitRainData(void)
      {
        int rainUpdateTotal = 0;
        for (int i = 0; i < 24; i++)
        {
          rainUpdateTotal += rainBucket[i];
        }
        gw.send(msgRainVAR1.set(( float) rainUpdateTotal / 100.0 , 1));
        delay(DWELL_TIME);
        rainUpdateTotal = 0;
        for (int i = 24; i < 48; i++)
        {
          rainUpdateTotal += rainBucket[i];
        }
        gw.send(msgRainVAR2.set( (float) rainUpdateTotal / 100.0 , 1));
        delay(DWELL_TIME);
        rainUpdateTotal = 0;
        for (int i = 48; i < 72; i++)
        {
          rainUpdateTotal += rainBucket[i];
        }
        gw.send(msgRainVAR3.set( (float) rainUpdateTotal / 100.0 , 1));
        delay(DWELL_TIME);
        rainUpdateTotal = 0;
        for (int i = 72; i < 96; i++)
        {
          rainUpdateTotal += rainBucket[i];
        }
        gw.send(msgRainVAR4.set( (float) rainUpdateTotal / 100.0 , 1));
        delay(DWELL_TIME);
        rainUpdateTotal = 0;
        for (int i = 96; i < 120; i++)
        {
          rainUpdateTotal += rainBucket[i];
        }
        gw.send(msgRainVAR5.set( (float) rainUpdateTotal / 100.0 , 1));
        delay(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 from controller..."));
        setTime(time);
        char theTime[26];
        sprintf(theTime, "The current time is %d:%2d", hour(), minute());
        DEBUG_PRINTLN(theTime);
      }
      
      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: problem in SoilMoistSensor

      @Reza

      Well, I really don't like a few things in that sketch, like the struct that is never used, for example.

      or using a global variable as an array index:

      int i; // Simple index variable
      

      or superfluous nonsense comments:

      // initialize serial communications at 9600 bps:
        Serial.begin(115200);
      

      The other thing is that the "average" function may be using junk until you take 10 readings, and that takes 5 minutes to clear out:

      long buffer[NUM_READS];
      

      try this instead which will initialize the array to zeroes:

      long buffer[NUM_READS] = {0};
      

      also, we ought to modify the addReading() and average() functions to size itself for the first 9 readings....

      There may be other datatype issues, but let's start here.

      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Rain Guage

      @ServiceXp

      waiting for the 3D prints from China, I'll post when I get them!

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Adding 4th dimmer. Struggle.

      @moskovskiy82

      Sorry, I meant that your code could be extremely simplified by using arrays.

      Yours is a good example of how it is much harder to refactor in the case (like yours) where you simply need to add one more of some object.

      things like this can be problematic:

      #define DIMMER_NODE_1 0
      #define DIMMER_NODE_2 1
      #define DIMMER_NODE_3 2
      #define DIMMER_NODE_4 3 //ADDED
      

      where 1 = 0, 2 = 1, etc.... it is not very intuitive

      I cannot find the problem in your code... are you sure you are wired up correctly?

      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Rain Guage

      @marceltrapman said:

      I would love to see the .stl files for this.
      It has turned into something really amazing imho.

      I'll post, but I wanted to print and check first...

      @marceltrapman here you go...

      Posted to GoogleDrive

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Adding 4th dimmer. Struggle.

      @rejoe2 said in Adding 4th dimmer. Struggle.:

      I completely agree with @BulldogLowell : Better use arrays for nodes like yours. Good example: https://forum.mysensors.org/topic/4847/multi-button-relay-sketch/33#

      and I don't like the blocking nature of the fading. that should be refactored.

      plus, the fyi the Bounce2 library has another constructor which sets pinMode...
      instead of this:

        pinMode(BUTTON_PIN1,INPUT);
        digitalWrite(BUTTON_PIN1, HIGH);
      
      //...
      debouncer_1.attach(BUTTON_PIN1);
        debouncer_1.interval(5);
      

      just this:

      debouncer_1.attach(BUTTON_PIN1, INPUT_PULLUP);
      debouncer_1.interval(5);
      

      i'll post an alternative if I get the chance...

      posted in Troubleshooting
      BulldogLowell
      BulldogLowell
    • RE: Irrigation Controller (up to 16 valves with Shift Registers)

      @treb0r said:

      @BulldogLowell
      As I understand these timers are only for manuals starts (via button) right?

      Well, those times will be used when you activate the device using the Domoticz on/off controls (or activate using 'scene' in Domotocz.

      Give me a day or so and I will try to code for you.

      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Binary Control Modules , Switches, or Outlets on a Vera Edge

      @mntlvr said:

      So if anyone is interested in the Vera coding or the sketch coding let me know, and I will try to help you.

      Great to see that you have your rig working just the way you like!

      You can simplify that code a lot with the use of arrays, for example (compiles but not tested):

      #include <MySensor.h>
      #include <SPI.h>
      #include <Bounce2.h>
      
      #define NUMBER_OF_PINS 6
      
      MySensor gw;
      int buttonPin[NUMBER_OF_PINS] = {3,4,5,6,7,8};
      Bounce debouncer[NUMBER_OF_PINS] = Bounce();
      int oldValue[NUMBER_OF_PINS];
      MyMessage msg[NUMBER_OF_PINS];  /* or you can fashion the constructor like this:*/  //= {MyMessage(0,V_TRIPPED),MyMessage(1,V_TRIPPED),MyMessage(2,V_TRIPPED),MyMessage(3,V_TRIPPED),MyMessage(4,V_TRIPPED),MyMessage(5,V_TRIPPED)};
      
      void setup() 
      {
        gw.begin();
        gw.sendSketchInfo("RemoteControl", "1.05");
        for (int i = 0; i < NUMBER_OF_PINS; i++)
        {
          msg[i] = MyMessage(i, V_TRIPPED); // instead of this
          gw.present(i, S_DOOR);
          pinMode(buttonPin[i], INPUT_PULLUP);
          debouncer[buttonPin[i]].attach(buttonPin[i]);
          debouncer[buttonPin[i]].interval(5);
        }
      }
      
      void loop() 
      {
        for (int i = 0; i < NUMBER_OF_PINS; i++)
        {
          debouncer[i].update();
          int value = debouncer[i].read();
          if (value != oldValue[i])
          {
            gw.send(msg[i].set(value));
            oldValue[i] = value;
          }
        }
      }
      
      posted in My Project
      BulldogLowell
      BulldogLowell
    • RE: Video How To - Phoney TV

      I had to update my firmware for MySensors version 2.0.0, I thought I would post it in case anyone comes across this post...

      /*
       * PhoneyTV v3.2.0
       *
       * This Sketch illuminates 6 sets of LED's in a random fashion as to mimic the
       * light eminating from a television.  It is intended to make an empty home,
       * or an empty section of a home, appear to be occupied by someone watching
       * TV.  As an alternative to a real television left on, this uses less than 1%
       * of the electrical energy.
       *
       * With the use of the MySensors plugin and gateway, PhoneyTV is intended to
       * be used with a controller (e.g. Vera or Raspberry PI).
       *
       * Sketch does not use any delays to create the random blinking as a way to
       * assure that communication back to the gateway is as unaffected as possible.
       *
       * You can adjust the length of the blink interval and its "twitchyness" by
       * modifying the random number generators, if you prefer more/less 'motion' in
       * in your unit.  The lines are highlighted in the code, play around to create the
       * random effect you like.
       *
       * Sketch takes advantage of available PWM on pins 3, 5 & 6 using the white/blue LEDs
       * to allow fluctuations in the intensity of the light, enhancing the PhoneyTV's
       * realistic light effects.
       *
       * Created 12-APR-2014
       * Free for distrubution
       * Credit should be given to MySensors.org for their base code for relay control
       * and for the radio configuration.  Thanks Guys.
       *
       * 29-May-2014
       * Version 2:  Simplified the code, removing all redundant relay setup from original
       * code.  Added an on/off momentary pushputton option to be set up on pin 2.  Inproved
       * the dark dips for longer duration (can be configured) at intervals.
       *
       * 6-Jun-2015
       * Version 3.1
       * Updated for MySensors V1.4.1
       * Contributed by Jim (BulldogLowell@gmail.com) Inspired by Josh >> Deltanu1142@gmail.com
       *
       * Version 3.2.0 updated for MySensors 2.0.0 by Jim (BulldogLowell@gmail.com)
       * How to video: https://youtu.be/p37qnl8Kjfc
       */
      // Enable debug prints to serial monitor
      #define MY_DEBUG 
      
      // Enable and select radio type attached
      #define MY_RADIO_NRF24
      //#define MY_RADIO_RFM69
      
      // Enable repeater functionality for this node
      //#define MY_REPEATER_FEATURE
      
      #include <SPI.h>
      #include <MySensors.h>
      #include <Bounce2.h>
      
      #define RADIO_RESET_DELAY_TIME 20
      
      #define DEBUG_ON
      
      #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
      
      const byte buttonPin = 2;
      const byte ledPin3 =  3;      // White using PWM
      const byte ledPin4 =  4;      // Red
      const byte ledPin5 =  5;      // Blue using PWM
      const byte ledPin6 =  6;      // Blue using PWM
      const byte ledPin7 =  7;      // Green
      const byte ledPin8 =  8;      // White (No PWM)
      
      Bounce debouncer = Bounce();
      byte oldValue = 0;
      boolean state = false;
      boolean oldState = false;
      int dipInterval = 10;
      unsigned int darkTime = 250;
      unsigned long currentDipTime;
      unsigned long dipStartTime;
      unsigned long currentMillis;
      byte ledState = LOW;
      unsigned long previousMillis = 0UL;
      byte led = 5;
      unsigned long interval = 2000UL;
      unsigned int twitch = 50;
      int dipCount = 0;
      int analogLevel = 100;
      boolean timeToDip = false;
      boolean gotAck=false;
      MyMessage msg(1,V_LIGHT);
      
      void before() 
      { 
        state = loadState(1)? 1 : 0;
      }
      
      
      void setup()
      {
        SERIAL_START(115200);
        pinMode(ledPin3, OUTPUT);
        pinMode(ledPin4, OUTPUT);
        pinMode(ledPin5, OUTPUT);
        pinMode(ledPin6, OUTPUT);
        pinMode(ledPin7, OUTPUT);
        pinMode(ledPin8, OUTPUT);
        pinMode(buttonPin, INPUT_PULLUP);
        //
        debouncer.attach(buttonPin);
        debouncer.interval(50);
        //
        DEBUG_PRINTLN(F("Sensor Presentation Complete"));
      }
      
      void presentation()  
      {   
        // Send the sketch version information to the gateway and Controller
        sendSketchInfo("PhoneyTV", "1.5");
        present(1, S_LIGHT);
      }
      
      void loop()
      {
        debouncer.update();
        byte value = debouncer.read();
        if (value != oldValue && value == 0)
        {
        state = !state;
        send(msg.set(state ? 1 : 0));
      //  while(!gotAck)
      //  {
      //    gw.send(msg.set(state), true);
      //    gw.wait(RADIO_RESET_DELAY_TIME);
      //  }
      //  gotAck = false;
        DEBUG_PRINT(F("State Changed to:"));
        DEBUG_PRINTLN(state? F("PhoneyTV ON") : F("PhoneyTV OFF"));
        }
        oldValue = value;
        if (state)
        {
        if (timeToDip == false)
        {
          currentMillis = millis();
          if (currentMillis - previousMillis > interval)
          {
          previousMillis = currentMillis;
          interval = random(750, 4001); //Adjusts the interval for more/less frequent random light changes
          twitch = random(40, 100); // Twitch provides motion effect but can be a bit much if too high
          dipCount++;
          }
          if (currentMillis - previousMillis < twitch)
          {
          led = random(3, 9);
          analogLevel = random(50, 255); // set the range of the 3 pwm leds
          ledState = !ledState;
          switch (led) //for the three PWM pins
          {
            case 3:
            pwmWrite();
            break;
            case 5:
            pwmWrite();
            break;
            case 6:
            pwmWrite();
            break;
            default:
            digitalWrite(led, ledState);
          }
          if (dipCount > dipInterval)
          {
            timeToDip = true;
            dipCount = 0;
            dipStartTime = millis();
            darkTime = random(50, 150);
            dipInterval = random(5, 250); // cycles of flicker
          }
          }
        }
        else
        {
          DEBUG_PRINTLN(F("Dip Time"));
          currentDipTime = millis();
          if (currentDipTime - dipStartTime < darkTime)
          {
          for (int i = 3; i < 9; i++)
          {
            digitalWrite(i, LOW);
          }
          }
          else
          {
          timeToDip = false;
          }
        }
        }
        else
        {
        if (state != oldState)
        {
          for (int i = 3; i < 9; i++)
          {
          digitalWrite(i, LOW);
          }
        }
        }
        oldState = state;
      }
      
      void receive(const MyMessage &message) {
        // We only expect one type of message from controller. But we better check anyway.
        if (message.type==V_LIGHT) {
           // Change relay state
           state = message.getBool()? 1 : 0;
           // Store state in eeprom
           saveState(message.sensor, message.getBool());
           // Write some debug info
           Serial.print("Incoming change for sensor:");
           Serial.print(message.sensor);
           Serial.print(", New status: ");
           Serial.println(message.getBool());
         } 
      }
      
      void pwmWrite()
      {
        if (ledState == HIGH) 
        {
        analogWrite(led, analogLevel);
        }
        else 
        {
        digitalWrite(led, LOW);
        }
      }
      
      posted in My Project
      BulldogLowell
      BulldogLowell
    • 3G Serial Bridge for Vera

      I wanted to share a project that I did for my Vera using the Particle Electron and a Nano... I think that the MySensors skill set transfers to Particle, so I'm hopeful some of you brave souls may try to pull this off!

      It is all documented here on my Git Repo. Ideas, suggestions and contributions welcome!

      0_1505768172165_VeraMainPanel.png

      0_1505768093992_VeraControlPanel.png

      0_1505768135162_ThreeGeeRocks.png

      I have a vacation home that I use Vera to control events. I, like a lot of other folks, have been a victim of:

      1. Power is out and I don't know.... I only find out when I try to log on 😠
      2. Vera is stalled, likewise, I only find out when I try to log on
      3. I've lost my connection the the internet. Again, how do I know that this happened? 😕

      I've recently developed a device (hardware & . Software) that allows me to detect all of these very difficult-to-detect events, for example:

      My Device lost Internet connectivity
      In this case, the device will detect the loss of internet and send a message via 3G network, circumventing the "How do I send a message if my Internet is down" paradox.

      My device (or home) has lost power
      In this case, the device will repeatedly remind me at a fixed interval until power is restored. The device has a LiPo battery which can send messages for days, even if there is no power.

      My Vera 3G gateway is not communicating with Vera
      If this happens you can create a notification which Vera can send

      My Vera is inactive, stalled or otherwise not properly functioning.
      When this happens, the 3G gateway can automatically power-cycle my Vera after some user-defined period of inactivity. (coming soon)

      The device can send SMS messages (during events as above or ad hoc by creating your own events using scenes or PLEG) or similarly use a service like Pushover to send a notification to any mobile (or desktop). I'm using Pushover direct to my Mobile device.

      I've developed a plugin that enables a new device to be set up in minutes.

      With such a configuration, I am using a 3G SIM/plan that costs $3.00 per month... about the cost of a cup of coffee. But you can use any SIM/plan you like.

      While I built mine on my own, because of the low operating cost, I realized that others may have an interest in building such a device. I was considering developing a PCB and providing a list of all the components necessary to build a device. I may even design a 3D printable case if there is enough interest.

      So, I'm looking to see if there is any interest and based on that, provide (sell) a kit that anyone can use. So, if you have an interest please let me know by responding to this post. If you are great at PCB dev, then even better, I can use your help!

      It is cool... and just what many of forum participants have been asking for.

      posted in My Project
      BulldogLowell
      BulldogLowell