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

Dependencies:   EthernetNetIf mbed HTTPServer

Files at this revision

API Documentation at this revision

Comitter:
dminear
Date:
Tue Mar 08 15:22:44 2011 +0000
Child:
1:91c2c52b4691
Commit message:
Initial Rev

Changed in this revision

EthernetNetIf.lib Show annotated file Show diff for this revision Revisions of this file
HTTPServer.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
ntp/NTPClient.cpp Show annotated file Show diff for this revision Revisions of this file
ntp/NTPClient.h Show annotated file Show diff for this revision Revisions of this file
share.h Show annotated file Show diff for this revision Revisions of this file
sprinkler_handler.cpp Show annotated file Show diff for this revision Revisions of this file
sprinkler_handler.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EthernetNetIf.lib	Tue Mar 08 15:22:44 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mamezu/code/EthernetNetIf/#0f6c82fcde82
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPServer.lib	Tue Mar 08 15:22:44 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/donatien/code/HTTPServer/#d753966e4d97
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Mar 08 15:22:44 2011 +0000
@@ -0,0 +1,117 @@
+#include "mbed.h"
+#include "EthernetNetIf.h"
+#include "HTTPServer.h"
+#include "sprinkler_handler.h"
+#include "share.h"
+#include "NTPClient.h"
+
+NTPClient ntp;
+
+DigitalOut led1(LED1, "led1");
+DigitalOut led2(LED2, "led2");
+DigitalOut led3(LED3, "led3");
+DigitalOut led4(LED4, "led4");
+DigitalOut ledgreen(p29);
+DigitalOut ledyellow(p30);
+
+LocalFileSystem fs("webfs");
+
+EthernetNetIf eth;  
+HTTPServer svr;
+Timer activity_timer;
+
+DigitalIn bypass(p16);
+DigitalIn zone1in(p17);
+DigitalIn zone2in(p18);
+DigitalIn zone3in(p19);
+DigitalIn zone4in(p20);
+
+DigitalOut zone1out(p24);
+DigitalOut zone2out(p23);
+DigitalOut zone3out(p22);
+DigitalOut zone4out(p21);
+
+int compute1out = 0;
+int compute2out = 0;
+int compute3out = 0;
+int compute4out = 0;
+
+void lanActivity( void ) {
+    activity_timer.start();
+    ledgreen = 0;
+}
+
+int main() {
+    //Base::add_rpc_class<DigitalOut>();
+    ledyellow = 0;
+    ledgreen = 0;
+    activity_timer.reset();
+
+    printf("Setting up...\n");
+    EthernetErr ethErr = eth.setup();
+    if(ethErr) {
+       printf("Error %d in setup.\n", ethErr);
+       return -1;
+    } else {
+       ledgreen = 1;
+    }
+
+  printf("Setup OK\n");
+  
+  FSHandler::mount("/webfs", "/files"); //Mount /webfs path on /files web path
+  FSHandler::mount("/webfs", "/"); //Mount /webfs path on web root path
+  
+  svr.addHandler<SimpleHandler>("/check");
+  // the main url with be http://a.b.c.d/sprinkler
+  // a GET request will return current status
+  // a POST with parameters of zone<n> and duration in seconds will set it
+  svr.addHandler<SprinklerHandler>("/sprinkler");
+  svr.addHandler<RPCHandler>("/rpc");
+  svr.addHandler<FSHandler>("/files");
+  svr.addHandler<FSHandler>("/"); //Default handler
+  //Example : Access to mbed.htm : http://a.b.c.d/mbed.htm or http://a.b.c.d/files/mbed.htm
+  
+  svr.bind(80);
+  
+  printf("Listening...\n");
+
+  Timer tm;
+  int timesync = 0;
+  tm.start();
+  time_t initialtime = time(NULL);
+  printf("Time as a basic string = %s\r", ctime(&initialtime));
+  //Listen indefinitely
+  while(true) {
+   //wait(0.01);
+    Net::poll();
+    if(tm.read()>0.5) {
+      ledyellow = !ledyellow; //Show that we are alive
+      tm.start();
+      timesync++;
+    }
+    if(activity_timer > 0.2) {
+        ledgreen = 1;
+    }
+    
+    if (timesync > 3600 * 2) {  // every hour
+            printf( "Setting time...\r\n" );
+            Host server2(IpAddr(), 123, "pool.ntp.org");
+            ntp.setTime(server2);
+            time_t seconds = time(NULL);
+            printf("Time as a basic string = %s\r", ctime(&seconds));
+    }    
+    
+    if (bypass) { // ignore all other choices other than inputs
+        led1 = zone1out = zone1in;
+        led2 = zone2out = zone2in;
+        led3 =  zone3out = zone3in;
+        led4 = zone4out = zone4in;
+    } else {    // take computed outputs
+        led1 = zone1out = compute1out;
+        led2 = zone2out = compute2out;
+        led3 = zone3out = compute3out;
+        led4 = zone4out = compute4out;
+    }
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Tue Mar 08 15:22:44 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/9a9732ce53a1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ntp/NTPClient.cpp	Tue Mar 08 15:22:44 2011 +0000
@@ -0,0 +1,335 @@
+
+/*
+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 "NTPClient.h"
+
+#include <stdio.h>
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+#define NTP_PORT 123
+#define NTP_CLIENT_PORT 0//50420 //Random port
+#define NTP_REQUEST_TIMEOUT 15000
+#define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
+
+#define htons( x ) ( (( x << 8 ) & 0xFF00) | (( x >> 8 ) & 0x00FF) )
+#define ntohs( x ) (htons(x))
+
+#define htonl( x ) ( (( x << 24 ) & 0xFF000000)  \
+                   | (( x <<  8 ) & 0x00FF0000)  \
+                   | (( x >>  8 ) & 0x0000FF00)  \
+                   | (( x >> 24 ) & 0x000000FF)  )
+#define ntohl( x ) (htonl(x))
+
+NTPClient::NTPClient() : NetService(false), m_state(NTP_PING), m_pCbItem(NULL), m_pCbMeth(NULL), m_pCb(NULL),
+m_watchdog(), m_timeout(0), m_closed(true), m_host(), m_pDnsReq(NULL), m_blockingResult(NTP_PROCESSING)
+{
+  setTimeout(NTP_REQUEST_TIMEOUT);
+  DBG("\r\nNew NTPClient %p\r\n",this);
+}
+
+NTPClient::~NTPClient()
+{
+  close();
+}
+
+//High level setup functions
+NTPResult NTPClient::setTime(const Host& host) //Blocking
+{
+  doSetTime(host);
+  return blockingProcess();
+}
+
+NTPResult NTPClient::setTime(const Host& host, void (*pMethod)(NTPResult)) //Non blocking
+{
+  setOnResult(pMethod);
+  doSetTime(host);
+  return NTP_PROCESSING;
+}
+
+#if 0 //For doc only
+template<class T> 
+NTPResult NTPClient::setTime(const Host& host, T* pItem, void (T::*pMethod)(NTPResult)) //Non blocking
+{
+  setOnResult(pItem, pMethod);
+  doSetTime(host);
+  return NTP_PROCESSING;
+}
+#endif
+
+void NTPClient::doSetTime(const Host& host)
+{
+  init();
+  resetTimeout();
+  m_host = host;
+  if(!m_host.getPort())
+  {
+    m_host.setPort(NTP_PORT);
+  }
+  if(m_host.getIp().isNull())
+  {
+    //DNS query required
+    m_pDnsReq = new DNSRequest();
+    DBG("\r\nNTPClient : DNSRequest %p\r\n", m_pDnsReq);
+    m_pDnsReq->setOnReply(this, &NTPClient::onDNSReply);
+    m_pDnsReq->resolve(&m_host);
+    return;
+  }
+  open();
+}
+
+void NTPClient::setOnResult( void (*pMethod)(NTPResult) )
+{
+  m_pCb = pMethod;
+}
+
+void NTPClient::close()
+{
+  if(m_closed)
+    return;
+  m_closed = true; //Prevent recursive calling or calling on an object being destructed by someone else
+  m_watchdog.stop();
+  m_pUDPSocket->resetOnEvent();
+  m_pUDPSocket->close();
+  delete m_pUDPSocket;
+  if( m_pDnsReq )
+  {
+    m_pDnsReq->close();
+    delete m_pDnsReq;
+    m_pDnsReq = NULL;
+  }
+}
+
+void NTPClient::poll() //Called by NetServices
+{
+  if( (!m_closed) && (m_watchdog.read_ms() >= m_timeout) )
+  {
+    onTimeout();
+  }
+}
+
+void NTPClient::init() //Create and setup socket if needed
+{
+  if(!m_closed) //Already opened
+    return;
+  m_state = NTP_PING;
+  m_pUDPSocket = new UDPSocket;
+  m_pUDPSocket->setOnEvent(this, &NTPClient::onUDPSocketEvent);
+  m_closed = false;
+  DBG("NTPClient: Init OK\n");
+}
+
+void NTPClient::open()
+{
+  resetTimeout();
+  DBG("Opening connection\n");
+  m_state = NTP_PING;
+  Host localhost(IpAddr(), NTP_CLIENT_PORT, "localhost"); //Any local address
+  m_pUDPSocket->bind(localhost);
+  set_time( 1280000000 ); //End of July 2010... just there to limit offset range
+  process();
+}
+
+#define MIN(a,b) ((a)<(b))?(a):(b)
+void NTPClient::process() //Main state-machine
+{
+  int len;
+  Host host;
+  
+  switch(m_state)
+  {
+  case NTP_PING:
+    DBG("\r\nPing\r\n");
+    //Prepare NTP Packet:
+    m_pkt.li = 0; //Leap Indicator : No warning
+    m_pkt.vn = 4; //Version Number : 4
+    m_pkt.mode = 3; //Client mode
+    m_pkt.stratum = 0; //Not relevant here
+    m_pkt.poll = 0; //Not significant as well
+    m_pkt.precision = 0; //Neither this one is
+    
+    m_pkt.rootDelay = 0; //Or this one
+    m_pkt.rootDispersion = 0; //Or that one
+    m_pkt.refId = 0; //...
+    
+    m_pkt.refTm_s = 0;
+    m_pkt.origTm_s = 0;
+    m_pkt.rxTm_s = 0;
+    m_pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
+    
+    m_pkt.refTm_f = m_pkt.origTm_f = m_pkt.rxTm_f = m_pkt.txTm_f = 0;
+    
+    #ifdef __DEBUG
+    //Hex Dump:
+    DBG("\r\nDump Tx:\r\n");
+    for(int i = 0; i< sizeof(NTPPacket); i++)
+    {
+      DBG("%02x ", *((char*)&m_pkt + i));
+    }
+    DBG("\r\n\r\n");
+    #endif
+    
+    len = m_pUDPSocket->sendto( (char*)&m_pkt, sizeof(NTPPacket), &m_host );
+    if(len < sizeof(NTPPacket))
+      { onResult(NTP_PRTCL); close(); return; }
+      
+    m_state = NTP_PONG; 
+          
+    break;
+  
+  case NTP_PONG:
+    DBG("\r\nPong\r\n");
+    while( len = m_pUDPSocket->recvfrom( (char*)&m_pkt, sizeof(NTPPacket), &host ) )
+    {
+      if( len <= 0 )
+        break;
+      if( !host.getIp().isEq(m_host.getIp()) )
+        continue; //Not our packet
+      if( len > 0 )
+        break;
+    }
+    
+    if(len == 0)
+      return; //Wait for the next packet
+    
+    if(len < 0)
+      { onResult(NTP_PRTCL); close(); return; }
+    
+    if(len < sizeof(NTPPacket)) //TODO: Accept chunks
+      { onResult(NTP_PRTCL); close(); return; }
+      
+    #ifdef __DEBUG
+    //Hex Dump:
+    DBG("\r\nDump Rx:\r\n");
+    for(int i = 0; i< sizeof(NTPPacket); i++)
+    {
+      DBG("%02x ", *((char*)&m_pkt + i));
+    }
+    DBG("\r\n\r\n");
+    #endif
+      
+    if( m_pkt.stratum == 0)  //Kiss of death message : Not good !
+    {
+      onResult(NTP_PRTCL); close(); return;
+    }
+    
+    //Correct Endianness
+    m_pkt.refTm_s = ntohl( m_pkt.refTm_s );
+    m_pkt.refTm_f = ntohl( m_pkt.refTm_f );
+    m_pkt.origTm_s = ntohl( m_pkt.origTm_s );
+    m_pkt.origTm_f = ntohl( m_pkt.origTm_f );
+    m_pkt.rxTm_s = ntohl( m_pkt.rxTm_s );
+    m_pkt.rxTm_f = ntohl( m_pkt.rxTm_f );
+    m_pkt.txTm_s = ntohl( m_pkt.txTm_s );
+    m_pkt.txTm_f = ntohl( m_pkt.txTm_f );
+    
+    //Compute offset, see RFC 4330 p.13
+    uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
+    //int32_t origTm = (int32_t) ((uint64_t) m_pkt.origTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
+    //int32_t rxTm = (int32_t) ((uint64_t) m_pkt.rxTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
+    //int32_t txTm = (int32_t) ((uint64_t) m_pkt.txTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
+   // int64_t offset = ( ( ( m_pkt.rxTm_s - m_pkt.origTm_s ) + ( m_pkt.txTm_s - destTm_s ) ) << 32 + ( ( m_pkt.rxTm_f - m_pkt.origTm_f ) + ( m_pkt.txTm_f - 0 ) ) ) / 2;
+    int64_t offset = ( (int64_t)( m_pkt.rxTm_s - m_pkt.origTm_s ) + (int64_t) ( m_pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
+    DBG("\r\nSent @%d\r\n", m_pkt.txTm_s);
+    DBG("\r\nOffset: %d\r\n", offset);
+    //Set time accordingly
+    set_time( time(NULL) + (offset /*>> 32*/) );
+    
+    onResult(NTP_OK);
+    close();
+      
+    break;
+  }
+}
+
+void NTPClient::setTimeout(int ms)
+{
+  m_timeout = ms;
+}
+
+void NTPClient::resetTimeout()
+{
+  m_watchdog.reset();
+  m_watchdog.start();
+}
+
+void NTPClient::onTimeout() //Connection has timed out
+{
+  close();
+  onResult(NTP_TIMEOUT);
+}
+
+void NTPClient::onDNSReply(DNSReply r)
+{
+  if(m_closed)
+  {
+    DBG("\r\nWARN: Discarded\r\n");
+    return;
+  }
+  
+  if( r != DNS_FOUND )
+  {
+    DBG("\r\nCould not resolve hostname.\r\n");
+    onResult(NTP_DNS);
+    close();
+    return;
+  }
+  DBG("\r\nDNS resolved.\r\n");
+  m_pDnsReq->close();
+  delete m_pDnsReq;
+  m_pDnsReq=NULL;
+  open();
+}
+  
+void NTPClient::onUDPSocketEvent(UDPSocketEvent e)
+{
+  resetTimeout();
+  switch(e)
+  {
+  case UDPSOCKET_READABLE: //The only event for now
+    resetTimeout();
+    process();
+    break;
+  }
+}
+
+void NTPClient::onResult(NTPResult r) //Must be called by impl when the request completes
+{
+  if(m_pCbItem && m_pCbMeth)
+    (m_pCbItem->*m_pCbMeth)(r);
+  else if(m_pCb)
+    m_pCb(r);
+  m_blockingResult = r; //Blocking mode
+}
+
+NTPResult NTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available
+{
+  m_blockingResult = NTP_PROCESSING;
+  do
+  {
+    Net::poll();
+  } while(m_blockingResult == NTP_PROCESSING);
+  Net::poll(); //Necessary for cleanup
+  return m_blockingResult;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ntp/NTPClient.h	Tue Mar 08 15:22:44 2011 +0000
@@ -0,0 +1,200 @@
+
+/*
+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.
+*/
+
+/** \file
+NTP Client header file
+*/
+
+#ifndef NTP_CLIENT_H
+#define NTP_CLIENT_H
+
+#include "core/net.h"
+#include "core/netservice.h"
+#include "api/UDPSocket.h"
+#include "api/DNSRequest.h"
+#include "mbed.h"
+
+///NTP Client results
+enum NTPResult
+{
+  NTP_OK, ///<Success
+  NTP_PROCESSING, ///<Processing
+  NTP_PRTCL, ///<Protocol error
+  NTP_TIMEOUT, ///<Connection timeout
+  NTP_DNS ///<Could not resolve DNS hostname
+};
+
+///A NTP Client
+/**
+The NTP client is a simple UDP client that will update the mbed's RTC
+*/
+class NTPClient : protected NetService
+{
+public:
+  /**
+  Instantiates the NTP client
+  */
+  NTPClient();
+  virtual ~NTPClient();
+  
+  //High level setup functions
+  
+  ///Gets current time (blocking)
+  /**
+  Updates the time using the server host
+  Blocks until completion
+  @param host : NTP server
+  */
+  NTPResult setTime(const Host& host); //Blocking
+  
+  ///Gets current time (non-blocking)
+  /**
+  Updates the time using the server host
+  The function returns immediately and calls the callback on completion or error
+  @param host : NTP server
+  @param pMethod : callback function
+  */
+  NTPResult setTime(const Host& host, void (*pMethod)(NTPResult)); //Non blocking
+  
+  ///Gets current time (non-blocking)
+  /**
+  Updates the time
+  @param host : NTP server
+  @param pItem : instance of class on which to execute the callback method
+  @param pMethod : callback method
+  The function returns immediately and calls the callback on completion or error
+  */
+  template<class T> 
+  NTPResult setTime(const Host& host, T* pItem, void (T::*pMethod)(NTPResult)) //Non blocking
+  {
+    setOnResult(pItem, pMethod);
+    doSetTime(host);
+    return NTP_PROCESSING;
+  }
+  
+  ///Gets current time (non-blocking)
+  /**
+  Updates the time using the server host
+  The function returns immediately and calls the previously set callback on completion or error
+  @param host : NTP server
+  */
+  void doSetTime(const Host& host);
+  
+  ///Setups the result callback
+  /**
+  @param pMethod : callback function
+  */
+  void setOnResult( void (*pMethod)(NTPResult) );
+  
+  ///Setups the result callback
+  /**
+  @param pItem : instance of class on which to execute the callback method
+  @param pMethod : callback method
+  */
+  class CDummy;
+  template<class T> 
+  void setOnResult( T* pItem, void (T::*pMethod)(NTPResult) )
+  {
+    m_pCbItem = (CDummy*) pItem;
+    m_pCbMeth = (void (CDummy::*)(NTPResult)) pMethod;
+  }
+  
+  void close();
+  
+protected:
+  virtual void poll(); //Called by NetServices
+  
+private:
+  void init();
+  void open();
+  
+  __packed struct NTPPacket //See RFC 4330 for Simple NTP
+  {
+    //WARN: We are in LE! Network is BE!
+    //LSb first
+    unsigned mode : 3;
+    unsigned vn : 3;
+    unsigned li : 2;
+    
+    uint8_t stratum;
+    uint8_t poll;
+    uint8_t precision;
+    //32 bits header
+    
+    uint32_t rootDelay;
+    uint32_t rootDispersion;
+    uint32_t refId;
+    
+    uint32_t refTm_s;
+    uint32_t refTm_f;
+    uint32_t origTm_s;
+    uint32_t origTm_f;
+    uint32_t rxTm_s;
+    uint32_t rxTm_f;
+    uint32_t txTm_s;
+    uint32_t txTm_f;
+  };
+
+  void process(); //Main state-machine
+
+  void setTimeout(int ms);
+  void resetTimeout();
+  
+  void onTimeout(); //Connection has timed out
+  void onDNSReply(DNSReply r);
+  void onUDPSocketEvent(UDPSocketEvent e);
+  void onResult(NTPResult r); //Called when exchange completed or on failure
+  
+  NTPResult blockingProcess(); //Called in blocking mode, calls Net::poll() until return code is available
+
+  UDPSocket* m_pUDPSocket;
+
+  enum NTPStep
+  {
+    NTP_PING,
+    NTP_PONG
+  };
+  
+  NTPStep m_state;
+  
+  NTPPacket m_pkt;
+  
+  CDummy* m_pCbItem;
+  void (CDummy::*m_pCbMeth)(NTPResult);
+  
+  void (*m_pCb)(NTPResult);
+  
+  Timer m_watchdog;
+  int m_timeout;
+  
+  bool m_closed;
+  
+  Host m_host;
+  
+  DNSRequest* m_pDnsReq;
+  
+  NTPResult m_blockingResult; //Result if blocking mode
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share.h	Tue Mar 08 15:22:44 2011 +0000
@@ -0,0 +1,14 @@
+#ifndef SHARE_H
+#define SHARE_H
+
+extern Timer timer;
+
+extern int compute1out;
+extern int compute2out;
+extern int compute3out;
+extern int compute4out;
+
+//prototypes
+void lanActivity( void );
+
+#endif
\ No newline at end of file
--- /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();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sprinkler_handler.h	Tue Mar 08 15:22:44 2011 +0000
@@ -0,0 +1,52 @@
+/*
+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.
+*/
+
+#ifndef SPRINKLER_HANDLER_H
+#define SPRINKLER_HANDLER_H
+
+#include "../HTTPRequestHandler.h"
+#include "mbed.h"
+
+class SprinklerHandler : public HTTPRequestHandler
+{
+public:
+  SprinklerHandler(const char* rootPath, const char* path, TCPSocket* pTCPSocket);
+  virtual ~SprinklerHandler();
+
+//protected:
+  static inline HTTPRequestHandler* inst(const char* rootPath, const char* path, TCPSocket* pTCPSocket) { return new SprinklerHandler(rootPath, path, pTCPSocket); } //if we ever could do static virtual functions, this would be one
+  int parsekv( char **from, char *to, int max );
+  virtual void doGet();
+  virtual void doPost();
+  virtual void doHead();
+  
+  virtual void onReadable(); //Data has been read
+  virtual void onWriteable(); //Data has been written & buf is free
+  virtual void onClose(); //Connection is closing
+  
+  void turnOffZone1();
+  void turnOffZone2();
+  void turnOffZone3();
+  void turnOffZone4();
+};
+
+#endif