A sprinkler controller that takes HTTP command for zone and duration.

Dependencies:   EthernetNetIf mbed HTTPServer

Revision:
0:810ec1936452
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sprinkler_handler.cpp	Tue Mar 08 15:22:44 2011 +0000
@@ -0,0 +1,280 @@
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "sprinkler_handler.h"
+#include "share.h"
+#include <string>
+#include <cstring>
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+// there are only 4 zones for this sprinkler controller
+Timer zone1Timer;   // timers are used here, and they only go for about 30 minutes
+Timer zone2Timer;   // so this is not good for hours...
+Timer zone3Timer;
+Timer zone4Timer;
+float zone1Interval;
+float zone2Interval;
+float zone3Interval;
+float zone4Interval;
+Timeout zone1Timeout;
+Timeout zone2Timeout;
+Timeout zone3Timeout;
+Timeout zone4Timeout;
+
+SprinklerHandler::SprinklerHandler(const char* rootPath, 
+                                const char* path, 
+                                TCPSocket* pTCPSocket) : HTTPRequestHandler(rootPath,
+                                                                        path,
+                                                                        pTCPSocket)
+{
+
+}
+
+SprinklerHandler::~SprinklerHandler()
+{
+  DBG("\r\nHandler destroyed\r\n");
+}
+
+void SprinklerHandler::doGet()
+{
+  char resp[300];
+ 
+  DBG("\r\nIn SprinklerHandler::doGet()\r\n");  
+  lanActivity();
+  // just output the current timer values for each zone
+  const char * match;
+  if ((match = strstr( path().c_str(), "zone1")) > 0) {
+        sprintf( resp, "zone1=%f\n", zone1Timer.read() );
+  } else if ((match = strstr( path().c_str(), "zone2")) > 0 ) {
+        sprintf(  resp, "zone2=%f\n", zone2Timer.read() );
+  } else if ((match = strstr( path().c_str(), "zone3")) > 0 ) {
+       sprintf(  resp, "zone3=%f\n", zone3Timer.read() );
+  } else if ((match = strstr( path().c_str(), "zone4")) > 0 ) {
+        sprintf(  resp, "zone4=%f\n", zone4Timer.read() );
+  } else {      // assume they want all the status
+       sprintf( resp, "zone1=%f\nzone2=%f\nzone3=%f\nzone4=%f\n",
+            zone1Timer.read(), zone2Timer.read(), zone3Timer.read(), zone4Timer.read() );
+  }
+
+  setContentLen( strlen(resp) ); 
+  respHeaders()["Connection"] = "close";
+  respHeaders()["Content-type"] = "text/html";
+  writeData(resp, strlen(resp));
+  DBG("\r\nExit SprinklerHandler::doGet()\r\n");
+}
+
+void SprinklerHandler::doPost()
+{
+ char data[128];
+ string str = "POST path is " + path() + "\n";
+ int maxlen = dataLen();
+ if (maxlen > 127) { maxlen = 127; }
+ int len = readData( data, maxlen );
+ data[maxlen] = 0;
+ string d = data;
+ str += "and data is " + d + "\n";
+ 
+ lanActivity();
+ // find out if it's a reset or start
+ const char * match = strstr( path().c_str(), "reset");
+ if  (match > 0) {         // must be reset
+    // handle reset
+    zone1Timer.stop();
+    zone2Timer.stop();
+    zone3Timer.stop();
+    zone4Timer.stop();
+    compute1out = 0;
+    compute2out = 0;
+    compute3out = 0;
+    compute4out = 0;
+    // cancel any pending timer interrupts
+    zone1Timeout.detach();
+    zone2Timeout.detach();
+    zone3Timeout.detach();
+    zone4Timeout.detach();
+    zone1Interval = 0;
+    zone2Interval = 0;
+    zone3Interval = 0;
+    zone4Interval = 0;
+    str = "OK";
+    DBG("\r\nreset called\r\n");
+  } else if ((match = strstr( path().c_str(), "start")) > 0) {    // must be start
+    // get zone and duration from message - example:  zone=1&duration=300
+     map<string, string> params;
+    char line[128];
+    char key[128];
+    char value[128];
+    char * from = data;
+    while( parsekv(&from, line, 128) > 0) //if == 0, it is an empty line = end of headers
+        {
+         int n = sscanf(line, "%[^=]=%[^\n]", key, value);
+         if ( n == 2 )
+            {
+            DBG("Read params : %s : %s\r\n", key, value);
+             params[key] = value;
+            }
+        }   
+    // get duration
+    if (atoi(params["zone"].c_str()) < 1  || atoi(params["zone"].c_str()) > 4) {
+        str = "unknown zone";
+    } else {
+        if (atoi(params["duration"].c_str()) <= 0) {
+            str = "invalid duration";
+        } else {
+            int dur = atoi(params["duration"].c_str());
+            switch (atoi(params["zone"].c_str())) {
+                case 1:
+                    zone1Timer.reset();
+                    zone1Timer.start();
+                    compute1out = 1;
+                    zone1Interval = dur;
+                    zone1Timeout.attach( this, &SprinklerHandler::turnOffZone1, dur );
+                    break;
+                    
+                case 2:
+                    zone2Timer.reset();
+                    zone2Timer.start();
+                    compute2out = 1;
+                    zone2Interval = dur;
+                    zone2Timeout.attach( this, &SprinklerHandler::turnOffZone2, dur );
+                    break;
+                    
+                case 3:
+                    zone3Timer.reset();
+                    zone3Timer.start();
+                    compute3out = 1;
+                    zone3Interval = dur;
+                    zone3Timeout.attach( this, &SprinklerHandler::turnOffZone3, dur );
+                    break;
+                    
+                case 4:
+                    zone4Timer.reset();
+                    zone4Timer.start();
+                    compute4out = 1;
+                    zone4Interval = dur;
+                    zone4Timeout.attach( this, &SprinklerHandler::turnOffZone4, dur );
+                    break;
+                    
+                default:
+                    str = "bad zone in case statement";
+                    break;
+            }
+        }
+    }
+ 
+    DBG("\r\nstart called\r\n");
+  }
+  
+  const char * resp = str.c_str(); 
+  setContentLen( strlen(resp) );
+  respHeaders()["Content-type"] = "text/html";
+  respHeaders()["Connection"] = "close";
+  writeData(resp, strlen(resp));
+  DBG("\r\nExit SprinklerHandler::doPost()\r\n");
+}
+
+// taken from the HTTP parsing code
+int SprinklerHandler::parsekv( char **from, char *to, int max ) {
+    /*
+     * look for & or \n and return key=value line
+     */
+     char *end;
+     int retval = 0;
+     
+     end = strstr( *from, "&" );
+     if (end) {
+        int len = (end - *from);
+        strncpy( to, *from, len);
+        to[len] = 0;
+        *from += len + 1;
+        DBG("parsekv sending back %s\r\n", to);
+        retval = 1;
+     } else {
+        // maybe this was the last parameter
+        if (strstr(*from, "=")) {
+            strncpy( to, *from, strlen(*from));
+            to[strlen(*from)] = 0;
+            *from += strlen(*from);
+            DBG("parsekv sending back %s\r\n", to);
+            retval = 1;
+        }
+        // nope, nothing left, just fall through and return 0
+     }
+    return retval;
+}
+
+void SprinklerHandler::doHead()
+{
+
+}
+
+void SprinklerHandler::onReadable() //Data has been read
+{
+
+}
+
+void SprinklerHandler::onWriteable() //Data has been written & buf is free
+{
+  DBG("\r\nSimpleHandler::onWriteable() event\r\n");
+  close(); //Data written, we can close the connection
+}
+
+void SprinklerHandler::onClose() //Connection is closing
+{
+  //Nothing to do
+}
+
+void SprinklerHandler::turnOffZone1( void ) {
+    zone1Timeout.detach();
+    zone1Interval = 0;
+    compute1out = 0;
+    zone1Timer.stop();
+    zone1Timer.reset();
+}
+
+void SprinklerHandler::turnOffZone2( void ) {
+    zone2Timeout.detach();
+    zone2Interval = 0;
+    compute2out = 0;
+    zone2Timer.stop();
+    zone2Timer.reset();
+}
+
+void SprinklerHandler::turnOffZone3( void ) {
+    zone3Timeout.detach();
+    zone3Interval = 0;
+    compute3out = 0;
+    zone3Timer.stop();
+    zone3Timer.reset();
+}
+
+void SprinklerHandler::turnOffZone4( void ) {
+    zone4Timeout.detach();
+    zone4Interval = 0;
+    compute4out = 0;
+    zone4Timer.stop();
+    zone4Timer.reset();
+}
+
+