A sprinkler controller that takes HTTP command for zone and duration.
Dependencies: EthernetNetIf mbed HTTPServer
Diff: sprinkler_handler.cpp
- 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(); +} + +