Network Services

Dependents:   PwrCond_mbed5

Fork of W5500Interface_K22F by Andrew McCartney

Files at this revision

API Documentation at this revision

Comitter:
dgriffin65
Date:
Thu Jun 15 20:29:03 2017 +0000
Parent:
14:ada7d903623d
Commit message:
Converted to a single library

Changed in this revision

DHCPClient.lib Show diff for this revision Revisions of this file
DHCPClient/DHCPClient.cpp Show annotated file Show diff for this revision Revisions of this file
DHCPClient/DHCPClient.h Show annotated file Show diff for this revision Revisions of this file
DNSClient.lib Show diff for this revision Revisions of this file
DNSClient/DNSClient.cpp Show annotated file Show diff for this revision Revisions of this file
DNSClient/DNSClient.h Show annotated file Show diff for this revision Revisions of this file
DNSClient/dnsname.h Show annotated file Show diff for this revision Revisions of this file
DNSClient/pico_string.h Show annotated file Show diff for this revision Revisions of this file
HTTPD.lib Show diff for this revision Revisions of this file
HTTPD/CBuffer.h Show annotated file Show diff for this revision Revisions of this file
HTTPD/HTTPD.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPD/HTTPD.h Show annotated file Show diff for this revision Revisions of this file
HTTPD/HTTPD_req.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPD/HTTPD_util.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPD/HTTPD_ws.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPD/sha1.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPD/sha1.h Show annotated file Show diff for this revision Revisions of this file
SNTPClient.lib Show diff for this revision Revisions of this file
SNTPClient/SNTPClient.cpp Show annotated file Show diff for this revision Revisions of this file
SNTPClient/SNTPClient.h Show annotated file Show diff for this revision Revisions of this file
WebSocketClient.lib Show diff for this revision Revisions of this file
WebSocketClient/Websocket.cpp Show annotated file Show diff for this revision Revisions of this file
WebSocketClient/Websocket.h Show annotated file Show diff for this revision Revisions of this file
diff -r ada7d903623d -r 14382459c8b7 DHCPClient.lib
--- a/DHCPClient.lib	Thu Jun 15 20:25:40 2017 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-https://developer.mbed.org/teams/AMETEK-Powervar/code/DHCPClient/#9697009514d0
diff -r ada7d903623d -r 14382459c8b7 DHCPClient/DHCPClient.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DHCPClient/DHCPClient.cpp	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,208 @@
+// DHCPClient.cpp 2013/4/10
+#include "mbed.h"
+#include "mbed_debug.h"
+#include "UDPSocket.h"
+#include "DHCPClient.h"
+
+#define DBG_DHCP 0
+
+#if DBG_DHCP
+#define DBG(...) do{debug("[%s:%d]", __PRETTY_FUNCTION__,__LINE__);debug(__VA_ARGS__);} while(0);
+#define DBG_HEX(A,B) do{debug("[%s:%d]\r\n", __PRETTY_FUNCTION__,__LINE__);debug_hex(A,B);} while(0);
+#else
+#define DBG(...) while(0);
+#define DBG_HEX(A,B) while(0);
+#endif
+
+int DHCPClient::discover()
+{
+    m_pos = 0;
+    const uint8_t header[] = {0x01,0x01,0x06,0x00};
+    add_buf((uint8_t*)header, sizeof(header));
+    uint32_t x = time(NULL) + rand();
+    xid[0] = x>>24; xid[1] = x>>16; xid[2] = x>>8; xid[3] = x;
+    add_buf(xid, 4);
+    fill_buf(20, 0x00);
+    add_buf(chaddr, 6);
+    fill_buf(10+192, 0x00);
+    const uint8_t options[] = {0x63,0x82,0x53,0x63, // magic cookie
+                               53,1,DHCPDISCOVER,   // DHCP option 53: DHCP Discover
+                               55,4,1,3,15,6,
+                               255}; 
+    add_buf((uint8_t*)options, sizeof(options));
+    return m_pos;
+}
+
+int DHCPClient::request()
+{
+    m_pos = 0;
+    const uint8_t header[] = {0x01,0x01,0x06,0x00};
+    add_buf((uint8_t*)header, sizeof(header));
+    add_buf(xid, 4);
+    fill_buf(12, 0x00);
+    add_buf(siaddr, 4);
+    fill_buf(4, 0x00); // giaddr
+    add_buf(chaddr, 6);
+    fill_buf(10+192, 0x00);
+    const uint8_t options[] = {0x63,0x82,0x53,0x63, // magic cookie
+                               53,1,DHCPREQUEST,    // DHCP option 53: DHCP Request
+                               55,4,1,3,15,6,       // DHCP option 55:
+                               };
+    add_buf((uint8_t*)options, sizeof(options));
+    add_option(50, yiaddr, 4);
+    add_option(54, siaddr, 4);
+    add_option(255);
+    return m_pos;
+}
+
+int DHCPClient::offer(uint8_t buf[], int size) {
+    memcpy(yiaddr, buf+DHCP_OFFSET_YIADDR, 4);   
+    memcpy(siaddr, buf+DHCP_OFFSET_SIADDR, 4);   
+    uint8_t *p;
+    int msg_type = -1;
+    p = buf + DHCP_OFFSET_OPTIONS;
+    while(*p != 255 && p < (buf+size)) {
+        uint8_t code = *p++;
+        if (code == 0) { // Pad Option
+            continue;
+        }
+        int len = *p++;
+ 
+        DBG("DHCP option: %d\r\n", code);
+        DBG_HEX(p, len);
+
+        switch(code) {
+            case 53:
+                msg_type = *p;
+                break;
+            case 1:
+                memcpy(netmask, p, 4); // Subnet mask address
+                break;
+            case 3:
+                memcpy(gateway, p, 4); // Gateway IP address
+                break; 
+            case 6:  // DNS server
+                memcpy(dnsaddr, p, 4);
+                break;
+            case 51: // IP lease time 
+                break;
+            case 54: // DHCP server
+                memcpy(siaddr, p, 4);
+                break;
+        }
+        p += len;
+    }
+    return msg_type;
+}
+
+bool DHCPClient::verify(uint8_t buf[], int len) {
+    if (len < DHCP_OFFSET_OPTIONS) {
+        return false;
+    }
+    if (buf[DHCP_OFFSET_OP] != 0x02) {
+        return false;
+    }
+    if (memcmp(buf+DHCP_OFFSET_XID, xid, 4) != 0) {
+        return false;
+    }
+    return true;
+}
+
+void DHCPClient::callback()
+{
+    //printf("DHCPClient callback\n");
+    SocketAddress host;
+    int recv_len = m_udp->recvfrom(&host, (char*)m_buf, sizeof(m_buf));
+    if (recv_len < 0) {
+        return;
+    }
+    if (!verify(m_buf, recv_len)) {
+        return;
+    }
+    int r = offer(m_buf, recv_len);
+    if (r == DHCPOFFER) {
+        int send_size = request();
+        m_udp->sendto(m_server, (char*)m_buf, send_size);
+    } else if (r == DHCPACK) {
+        exit_flag = true;
+    }
+}
+
+void  DHCPClient::add_buf(uint8_t c)
+{
+    m_buf[m_pos++] = c;
+}
+
+void  DHCPClient::add_buf(uint8_t* buf, int len)
+{
+    for(int i = 0; i < len; i++) {
+        add_buf(buf[i]);
+    }
+}
+
+void DHCPClient::fill_buf(int len, uint8_t data)
+{
+    while(len-- > 0) {
+        add_buf(data);
+    }
+}
+
+void  DHCPClient::add_option(uint8_t code, uint8_t* buf, int len)
+{
+    add_buf(code);
+    if (len > 0) {
+        add_buf((uint8_t)len);
+        add_buf(buf, len);
+    }
+}
+
+int DHCPClient::setup(NetworkStack *ns, int timeout_ms)
+{
+    int interval_ms = 5*1000; // 5000msec
+    if (timeout_ms < interval_ms) {
+        interval_ms = timeout_ms;
+    }
+    UDPSocket udp_sock;
+    m_udp = &udp_sock;
+    
+    udp_sock.open(ns);
+    udp_sock.set_blocking(false);
+    udp_sock.bind(68); // local port
+    m_server.set_ip_address("255.255.255.255"); // DHCP broadcast
+    m_server.set_port(67);                      // DHCP broadcast
+    exit_flag = false;
+    int err = 0;
+    int seq = 0;
+    int send_size;
+    while(!exit_flag) {
+        switch(seq) {
+            case 0:
+                m_retry = 0;
+                seq++;
+                break;
+            case 1:
+                send_size = discover();
+                udp_sock.sendto(m_server, (char*)m_buf, send_size);
+                m_interval.reset();
+                m_interval.start();
+                seq++;
+                break;
+            case 2:
+                callback();
+                if (m_interval.read_ms() > interval_ms) {
+                    DBG("m_retry: %d\n", m_retry);
+                    if (++m_retry >= (timeout_ms/interval_ms)) {
+                        err = -1;
+                        exit_flag = true;
+                    }
+                    seq--;
+                }
+                break;
+        }
+    }
+    DBG("m_retry: %d, m_interval: %d\n", m_retry, m_interval.read_ms());
+    return err;
+}
+
+DHCPClient::DHCPClient() {
+}
diff -r ada7d903623d -r 14382459c8b7 DHCPClient/DHCPClient.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DHCPClient/DHCPClient.h	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,54 @@
+// DHCPClient.h 2013/4/10
+#ifndef DHCPCLIENT_H
+#define DHCPCLIENT_H
+#include "wiznet.h"
+#include "UDPSocket.h"
+
+#define DHCP_OFFSET_OP 0
+#define DHCP_OFFSET_XID 4
+#define DHCP_OFFSET_YIADDR 16
+#define DHCP_OFFSET_SIADDR 20
+#define DHCP_OFFSET_OPTIONS 240
+#define DHCP_MAX_PACKET_SIZE 600
+
+// DHCP Message Type
+#define DHCPDISCOVER 1
+#define DHCPOFFER    2
+#define DHCPREQUEST  3
+#define DHCPDECLINE  4
+#define DHCPACK      5
+#define DHCPNAK      6
+#define DHCPRELEASE  7
+#define DHCPINFORM   8
+
+class DHCPClient {
+public:
+    DHCPClient();
+    int setup(NetworkStack *ns, int timeout_ms = 15*1000);
+    uint8_t chaddr[6]; // MAC
+    uint8_t yiaddr[4]; // IP
+    uint8_t dnsaddr[4]; // DNS
+    uint8_t gateway[4];
+    uint8_t netmask[4];
+    uint8_t siaddr[4];
+private:
+    int discover();
+    int request();
+    int offer(uint8_t buf[], int size);
+    void add_buf(uint8_t* buf, int len);
+    void fill_buf(int len, uint8_t data = 0x00);
+    void add_buf(uint8_t c);
+    void add_option(uint8_t code, uint8_t* buf = NULL, int len = 0);
+    bool verify(uint8_t buf[], int len);
+    void callback();
+    UDPSocket* m_udp;
+    SocketAddress m_server;
+    uint8_t xid[4];
+    bool exit_flag;
+    Timer m_interval;
+    int m_retry;
+    uint8_t m_buf[DHCP_MAX_PACKET_SIZE];
+    int m_pos;
+    //WIZnet_Chip* eth;
+};
+#endif //DHCPCLIENT_H
diff -r ada7d903623d -r 14382459c8b7 DNSClient.lib
--- a/DNSClient.lib	Thu Jun 15 20:25:40 2017 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-https://developer.mbed.org/teams/AMETEK-Powervar/code/DNSClient/#06314bd0bca3
diff -r ada7d903623d -r 14382459c8b7 DNSClient/DNSClient.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DNSClient/DNSClient.cpp	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,245 @@
+// DNSClient.cpp 2013/8/27
+#include "mbed.h"
+#include "mbed_debug.h"
+#include "DNSClient.h"
+#include "UDPSocket.h"
+#include "dnsname.h"
+#include "wiznet.h"
+
+#if DBG_DNS
+#define DBG2(...) do{debug("[DNS]%p %d %s ", this,__LINE__,__PRETTY_FUNCTION__); debug(__VA_ARGS__); } while(0);
+#else
+#define DBG2(...) while(0);
+#endif
+
+static SocketAddress theDnsServer;
+
+DNSClient::DNSClient() : m_state(MYNETDNS_START), m_udp(NULL) {
+    m_ns = NULL;
+
+    //default to google
+    theDnsServer.set_ip_address("8.8.8.8"); // DNS
+    theDnsServer.set_port(53); // DNS
+}
+
+DNSClient::DNSClient(NetworkStack *ns, const char* hostname) : m_state(MYNETDNS_START), m_udp(NULL) {
+    m_hostname = hostname;
+    m_ns       = ns;
+    
+    //default to google
+    theDnsServer.set_ip_address("8.8.8.8"); // DNS
+    theDnsServer.set_port(53); // DNS
+}
+
+DNSClient::DNSClient(NetworkStack *ns, SocketAddress* pHost) : m_state(MYNETDNS_START), m_udp(NULL) {
+    m_ns = ns;
+
+    //default to google
+    theDnsServer.set_ip_address("8.8.8.8"); // DNS
+    theDnsServer.set_port(53); // DNS
+}
+
+DNSClient::~DNSClient() {
+    if (m_udp) {
+        delete m_udp;
+    }
+}
+
+int DNSClient::setup(NetworkStack *ns) {
+    m_ns = ns;
+    return 0;
+}
+
+bool DNSClient::set_server(const char* serverip, int port) {
+    theDnsServer.set_ip_address(serverip); 
+    theDnsServer.set_port(port);
+    return true;
+}
+
+void DNSClient::callback()
+{
+    uint8_t buf[512];
+    SocketAddress host;
+    int len = m_udp->recvfrom(&host, (char*)buf, sizeof(buf));
+    if (len < 0) {
+        return;
+    }
+    if (memcmp(buf+0, m_id, 2) != 0) { //verify
+        return;
+    }
+    
+    int rcode = response(buf, len);
+    if (rcode == 0) {
+        m_state = MYNETDNS_OK;
+    } else {
+        m_state = MYNETDNS_NOTFOUND;
+    }
+}
+
+int DNSClient::response(uint8_t buf[], int size) {
+    int rcode = buf[3] & 0x0f;
+    if (rcode != 0) {
+        return rcode;
+    }
+    int qdcount = buf[4]<<8|buf[5];
+    int ancount = buf[6]<<8|buf[7];
+    int pos = 12;
+    while(qdcount-- > 0) {
+        dnsname qname(buf);
+        pos = qname.decode(pos); // qname
+        pos += 4; // qtype qclass
+    }
+    while(ancount-- > 0) {
+        dnsname name(buf);
+        pos = name.decode(pos); // name
+        int type = buf[pos]<<8|buf[pos+1];
+        pos += 8; // type class TTL  
+        int rdlength = buf[pos]<<8|buf[pos+1]; pos += 2;
+        int rdata_pos = pos;
+        pos += rdlength;
+        if (type == 1) { // A record
+            m_ip = (buf[rdata_pos]<<24) | (buf[rdata_pos+1]<<16) | (buf[rdata_pos+2]<<8) | buf[rdata_pos+3];
+            sprintf(m_ipaddr, "%d.%d.%d.%d", buf[rdata_pos],buf[rdata_pos+1],buf[rdata_pos+2],buf[rdata_pos+3]);
+        }
+#if DBG_DNS
+        printf("%s", name.str.c_str());
+        if (type == 1) {
+            printf(" A %d.%d.%d.%d\n", 
+                buf[rdata_pos],buf[rdata_pos+1],buf[rdata_pos+2],buf[rdata_pos+3]);
+        } else if (type == 5) {
+            dnsname rdname(buf);
+            rdname.decode(rdata_pos);
+            printf(" CNAME %s\n", rdname.str.c_str());
+        } else {
+            printf(" TYPE:%d", type);
+            printfBytes(" RDATA:", &buf[rdata_pos], rdlength);
+        }
+#endif
+    }
+    return rcode;
+}
+
+int DNSClient::query(uint8_t buf[], int size, const char* hostname) {
+    const uint8_t header[] = {
+        0x00,0x00,0x01,0x00, // id=0x0000 QR=0 rd=1 opcode=0 rcode=0
+        0x00,0x01,0x00,0x00, // qdcount=1 ancount=0
+        0x00,0x00,0x00,0x00};// nscount=0 arcount=0 
+    const uint8_t tail[] = {0x00,0x01,0x00,0x01}; // qtype=A qclass=IN
+    memcpy(buf, header, sizeof(header));
+    int t = rand();
+    m_id[0] = t>>8;
+    m_id[1] = t;
+    memcpy(buf, m_id, 2); 
+    dnsname qname(buf);
+    int pos = qname.encode(sizeof(header), (char*)hostname);
+    memcpy(buf+pos, tail, sizeof(tail));
+    pos += sizeof(tail);
+    return pos;
+}
+
+void DNSClient::resolve(const char* hostname) {
+    if (m_udp == NULL) {
+        m_udp = new UDPSocket;
+        m_udp->open(m_ns);
+    }
+    m_udp->set_blocking(false);
+    m_udp->bind(rand()&0x7fff);
+    uint8_t buf[256];                
+    int size = query(buf, sizeof(buf), hostname);
+#if DBG_DNS
+    printf("hostname:[%s]\n", hostname);
+    printHex(buf, size);
+#endif
+    m_udp->sendto(theDnsServer, (char*)buf, size);
+    m_interval.reset();
+    m_interval.start();
+}
+
+void DNSClient::poll() {
+#if DBG_DNS
+    printf("%p m_state: %d, m_udp: %p\n", this, m_state, m_udp);
+    wait_ms(400);
+#endif
+    switch(m_state) {
+        case MYNETDNS_START:
+            m_retry = 0;
+            resolve(m_hostname);
+            m_state = MYNETDNS_PROCESSING;
+            break;
+        case MYNETDNS_PROCESSING: 
+            break;
+        case MYNETDNS_NOTFOUND: 
+            break;
+        case MYNETDNS_ERROR: 
+            break;
+        case MYNETDNS_OK:
+            DBG2("m_retry=%d, m_interval=%d\n", m_retry, m_interval.read_ms());
+            break;
+    }
+    if (m_interval.read_ms() > 1000) {
+        m_interval.stop();
+        DBG2("timeout m_retry=%d\n", m_retry);
+        if (++m_retry >= 2) {
+            m_state = MYNETDNS_ERROR;
+        } else {
+            resolve(m_hostname);
+            m_state = MYNETDNS_PROCESSING;
+        }
+    }
+}
+
+//tests for a valid IP address, returns 0 if invalid IP address format, returns ip address if valid
+static uint32_t
+isValidIP(const char* ip) {
+    int i1, i2, i3, i4;
+    int len = strlen(ip);
+
+    if (len < 7) return 0;
+
+    //count the number of dots, there must be 3
+    int dotcount = 0;
+    for (int i = 0; i < len; i++) {
+        if (ip[i] == '.' ) dotcount++;
+    }
+
+    //there must be exactly 3 dots
+    if (dotcount != 3) return 0;
+
+    sscanf((char*)ip, "%d.%d.%d.%d", &i1, &i2, &i3, &i4);
+
+    if (strlen(ip) <= 16 && (i1 >= 0 && i1 <= 255) && (i2 >= 0 && i2 <= 255) &&
+            (i3 >= 0 && i3 <= 255) && (i4 >= 0 && i4 <= 255)) {
+        return ((i1<<24) | (i2<<16) | (i3<<8) | i4);
+    }
+    return 0;
+}
+
+bool DNSClient::lookup(const char* hostname) {
+    m_hostname = hostname;
+    
+    uint32_t ip = isValidIP(hostname);
+
+    //check if hostname is an IP address
+    if (ip > 0) {
+        //if it is already an IP address just return immediately
+        m_ip = ip;
+        strcpy(m_ipaddr, hostname);
+        m_state = MYNETDNS_OK;
+        return true;
+    }
+    
+    m_state = MYNETDNS_START;
+    while(1) {
+        poll();
+        callback();
+        if (m_state != MYNETDNS_PROCESSING) {
+            break;
+        } 
+    }
+    
+    if (m_udp) {
+        delete m_udp;
+        m_udp = NULL;
+    }
+    return m_state == MYNETDNS_OK;
+}
diff -r ada7d903623d -r 14382459c8b7 DNSClient/DNSClient.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DNSClient/DNSClient.h	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,48 @@
+// DNSClient.h 2013/4/5
+#pragma once
+
+#include "UDPSocket.h"
+
+//#define DBG_DNS 1
+ 
+class DNSClient {
+public:
+    DNSClient();
+    DNSClient(NetworkStack *ns, const char* hostname = NULL);
+    DNSClient(NetworkStack *ns, SocketAddress* pHost);
+    
+    int setup(NetworkStack *ns);
+
+    virtual ~DNSClient();
+    bool lookup(const char* hostname);
+    bool set_server(const char* serverip, int port=53);
+    uint32_t get_ip() {return m_ip;}
+    const char* get_ip_address() {return m_ipaddr;}
+    
+protected:
+    void poll();
+    void callback();
+    int response(uint8_t buf[], int size);
+    int query(uint8_t buf[], int size, const char* hostname);
+    void resolve(const char* hostname);
+    uint8_t m_id[2];
+    Timer m_interval;
+    int m_retry;
+    const char* m_hostname;
+
+private:
+    enum MyNetDnsState
+    {
+        MYNETDNS_START,
+        MYNETDNS_PROCESSING, //Req has not completed
+        MYNETDNS_NOTFOUND,
+        MYNETDNS_ERROR,
+        MYNETDNS_OK
+    };
+    MyNetDnsState m_state;
+    UDPSocket *m_udp;
+    NetworkStack *m_ns;
+
+    uint32_t m_ip;
+    char m_ipaddr[24];
+};
diff -r ada7d903623d -r 14382459c8b7 DNSClient/dnsname.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DNSClient/dnsname.h	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,51 @@
+// dnsname.h 2013/8/27
+#pragma once
+//#include <string>
+#include "pico_string.h"
+class dnsname {
+public:
+    uint8_t *buf;
+    pico_string str;
+    dnsname(uint8_t *s) {
+        buf = s;
+    }
+    int decode(int pos) {
+        while(1) {
+            int len = buf[pos++];
+            if (len == 0x00) {
+                break;
+            }
+            if ((len&0xc0) == 0xc0) { //compress
+                int offset = (len&0x3f)<<8|buf[pos];
+                decode(offset);
+                return pos+1;
+            }
+            if (!str.empty()) {
+                str.append(".");
+            }
+            str.append((const char*)(buf+pos), len);
+            pos += len;
+        }
+        return pos;
+    }
+
+    int encode(int pos, char* s) {
+        while(*s) {  
+            char *f = strchr(s, '.');
+            if (f == NULL) {
+                int len = strlen(s);
+                buf[pos++] = len;
+                memcpy(buf+pos, s, len);
+                pos += len;
+                break;
+            }
+            int len = f - s;
+            buf[pos++] = len;
+            memcpy(buf+pos, s, len);
+            s = f+1;
+            pos += len;
+        }
+        buf[pos++] = 0x00;
+        return pos;
+    }
+};
diff -r ada7d903623d -r 14382459c8b7 DNSClient/pico_string.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DNSClient/pico_string.h	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,46 @@
+// pico_string.h 2013/8/27
+#pragma once
+class pico_string {
+public:
+    pico_string(){
+        _len = 0;
+        _buf = (char*)malloc(1);
+        if (_buf) {
+            _buf[0] = '\0';
+        }
+    }
+    ~pico_string() {
+        if (_buf) {
+            free(_buf);
+        }
+    }
+    bool empty() {
+        return _len == 0;
+    }
+    void append(const char* s, int len) {
+        if (_buf == NULL) {
+            return;
+        }
+        char* p = (char*)malloc(_len+len+1);
+        if (p == NULL) {
+            return;
+        }
+        memcpy(p, _buf, _len);
+        memcpy(p+_len, s, len);
+        p[_len+len] = '\0';
+        free(_buf);
+        _buf = p;
+    }
+    void append(const char* s) {
+        append(s, strlen(s));
+    }
+    char* c_str() {
+        if (_buf) {
+            return _buf;
+        }
+        return "";
+    }
+private:
+    char* _buf;
+    int _len;
+};
diff -r ada7d903623d -r 14382459c8b7 HTTPD.lib
--- a/HTTPD.lib	Thu Jun 15 20:25:40 2017 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-https://developer.mbed.org/teams/AMETEK-Powervar/code/HTTPD/#b724fdb741e7
diff -r ada7d903623d -r 14382459c8b7 HTTPD/CBuffer.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPD/CBuffer.h	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,75 @@
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * 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 CIRCBUFFER_H_
+#define CIRCBUFFER_H_
+
+template <class T>
+class CircBuffer {
+public:
+    CircBuffer(int length) {
+        write = 0;
+        read = 0;
+        size = length + 1;
+        buf = (T *)malloc(size * sizeof(T));
+    };
+
+    bool isFull() {
+        return (((write + 1) % size) == read);
+    };
+
+    bool isEmpty() {
+        return (read == write);
+    };
+
+    void queue(T k) {
+        if (isFull()) {
+            read++;
+            read %= size;
+        }
+        buf[write++] = k;
+        write %= size;
+    }
+    
+    void flush() {
+        read = 0;
+        write = 0;
+    }
+    
+
+    uint32_t available() {
+        return (write >= read) ? write - read : size - read + write;
+    };
+
+    bool dequeue(T * c) {
+        bool empty = isEmpty();
+        if (!empty) {
+            *c = buf[read++];
+            read %= size;
+        }
+        return(!empty);
+    };
+
+private:
+    volatile uint32_t write;
+    volatile uint32_t read;
+    uint32_t size;
+    T * buf;
+};
+
+#endif
diff -r ada7d903623d -r 14382459c8b7 HTTPD/HTTPD.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPD/HTTPD.cpp	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,137 @@
+/* Copyright (C) 2013 Hiroshi Suga, MIT License
+ *
+ * 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 "HTTPD.h"
+
+HTTPD * HTTPD::_inst;
+
+HTTPD::HTTPD () {
+    _inst = this;
+    memset(_state, 0, sizeof(_state));
+    _handler_count = 0;
+}
+
+int HTTPD::start (NetworkStack *ns, int port) {
+    int i;
+
+    m_ns = ns;
+    
+    for (i = 0; i < HTTPD_MAX_CLIENTS; i ++) {
+        _state[i].buf = new CircBuffer<char>(HTTPD_BUF_SIZE);
+        _state[i].thread = new Thread(osPriorityNormal, HTTPD_STACK_SIZE);
+        _state[i].client = new TCPSocket();
+        _state[i].thread->start(callback(child, (void*)i));
+    }
+    
+#ifdef HTTPD_ENABLE_CLOSER
+    _state[HTTPD_MAX_CLIENTS].thread = new Thread(closer, (void*)HTTPD_MAX_CLIENTS, osPriorityNormal, 128);
+    _state[HTTPD_MAX_CLIENTS].client = new TCPSocket(m_ns);
+#endif
+
+    _server.open(m_ns);
+    _server.bind(port);
+    _server.set_blocking(true);
+    _server.listen();
+    _daemon = new Thread(osPriorityNormal, HTTPD_STACK_SIZE);
+    _daemon->start(HTTPD::daemon);
+    return 0;
+}
+
+void HTTPD::daemon () {
+    HTTPD *httpd = HTTPD::getInstance();
+    int i, t = 0;
+
+    INFO("Wait for new connection...\r\n");
+    for (;;) {
+        if (t >= 0) {
+            if (httpd->_server.accept(httpd->_state[t].client) == 0) {
+                INFO("accept %d\r\n", t);
+                httpd->_state[t].thread->signal_set(1);
+            }
+        } else {
+#ifdef HTTPD_ENABLE_CLOSER
+            if (httpd->_server.accept(httpd->_state[HTTPD_MAX_CLIENTS].client) == 0) {
+                INFO("accept x\r\n");
+                httpd->_state[HTTPD_MAX_CLIENTS].thread->signal_set(1);
+            }
+#endif
+        }
+
+        t = -1;
+        for (i = 0; i < HTTPD_MAX_CLIENTS; i ++) {
+            if (httpd->_state[i].thread->get_state() == Thread::WaitingAnd) {
+                if (t < 0) t = i; // next empty thread
+            }
+        }
+    }
+}
+
+void HTTPD::child (void const *arg) {
+    HTTPD *httpd = HTTPD::getInstance();
+    int id = (int)arg;
+    int i, n;
+    char buf[HTTPD_BUF_SIZE];
+
+    for (;;) {
+        Thread::signal_wait(1);
+        httpd->_state[id].mode = MODE_REQUEST;
+        httpd->_state[id].buf->flush();
+        httpd->_state[id].keepalive = 0;
+        INFO("Connection from client\r\n");
+//      INFO("Connection from %s\r\n", httpd->_state[id].client->get_ip_address());
+
+        httpd->_state[id].client->set_blocking(false);
+        httpd->_state[id].client->set_timeout(HTTPD_TIMEOUT);
+        
+        for (;;) {
+            //if (! httpd->_state[id].client->is_connected()) break;
+
+            n = httpd->_state[id].client->recv(buf, sizeof(buf));
+            
+            if (n < 0 ) {
+                printf("HTTPD::child breaking n = %d\r\n", n);
+                break;
+            }
+            buf[n] = 0;
+            //DBG("Recv %d ", n);
+            DBG("Recv %d '%s'", n, buf);
+
+            for (i = 0; i < n; i ++) {
+                httpd->recvData(id, buf[i]);
+            }
+        }
+
+        httpd->_state[id].client->close();
+        INFO("Closed client connection\r\n");
+        //INFO("Close %s\r\n", httpd->_state[id].client->get_ip_address());
+    }
+}
+
+void HTTPD::closer (void const *arg) {
+    HTTPD *httpd = HTTPD::getInstance();
+    int id = (int)arg;
+
+    for (;;) {
+        Thread::signal_wait(1);
+
+        httpd->_state[id].client->close();
+        INFO("Closed client connection\r\n");
+        //INFO("Close %s\r\n", httpd->_state[id].client->get_ip_address());
+    }
+}
+
diff -r ada7d903623d -r 14382459c8b7 HTTPD/HTTPD.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPD/HTTPD.h	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,181 @@
+/* Copyright (C) 2013 Hiroshi Suga, MIT License
+ *
+ * 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 HTTPD_H
+#define HTTPD_H
+
+#include "mbed.h"
+#include "rtos.h"
+#include "NetworkStack.h"
+#include "EthInterface.h"
+#include "CBuffer.h"
+
+#define DEBUG
+
+#define HTTPD_PORT 80
+#define HTTPD_MAX_CLIENTS 5
+#define HTTPD_KEEPALIVE 10
+#define HTTPD_TIMEOUT 15000
+#define HTTPD_MAX_HANDLES 10
+ 
+#define HTTPD_CMD_SIZE 100
+#define HTTPD_BUF_SIZE 256
+#define HTTPD_STACK_SIZE (1024 * 6)
+//#define HTTPD_ENABLE_CLOSER
+
+//Debug is disabled by default
+#if defined(DEBUG) and (!defined(TARGET_LPC11U24))
+#define DBG(x, ...) std::printf("[DBG]" x "\r\n", ##__VA_ARGS__);
+#define WARN(x, ...) std::printf("[WARN]" x "\r\n", ##__VA_ARGS__);
+#define ERR(x, ...) std::printf("[ERR]" x "\r\n", ##__VA_ARGS__);
+#define INFO(x, ...) std::printf("[INFO]" x "\r\n", ##__VA_ARGS__);
+#else
+#define DBG(x, ...)
+#define WARN(x, ...)
+#define ERR(x, ...)
+#define INFO(x, ...)
+#endif
+
+
+class HTTPD {
+public:
+
+    enum Request {
+        REQ_HTTPGET,
+        REQ_HTTPPOST,
+        REQ_PUT,
+    };
+
+    enum Mode {
+        MODE_REQUEST,
+        MODE_REQSTR,
+        MODE_HEADER,
+        MODE_BODY,
+        MODE_ENTER,
+        MODE_ERROR,
+        MODE_WEBSOCKET,
+        MODE_WEBSOCKET_MASK,
+        MODE_WEBSOCKET_BODY,
+        MODE_WEBSOCKET_ENTER,
+    };
+
+    struct STATE {
+        Thread *thread;
+        TCPSocket *client;
+        volatile Request req;
+        volatile Mode mode;
+        CircBuffer <char>*buf;
+        char uri[HTTPD_CMD_SIZE];
+        char *filename;
+        char *querystring;
+        int enter;
+        int length;
+        int n;
+        int keepalive;
+        int websocket;
+        char *websocket_key;
+        int websocket_opcode;
+        int websocket_flg;
+        char websocket_mask[4];
+        int websocket_payload;
+        int (*sendws)(int id, const char *buf, int len, const char *mask);
+    };
+
+    HTTPD ();
+    
+    int start (NetworkStack *ns, int port = HTTPD_PORT);
+
+    // --- HTTPD_req.cpp ---
+    void httpdError (int id, int err);
+
+    // --- HTTPD_ws.cpp ---
+    static int sendWebsocket (int id, const char *buf, int len, const char *mask = NULL);
+
+    // --- HTTPD_util.cpp ---
+    char *getUri (int id);
+    char *getFilename (int id);
+    char *getQueryString (int id);
+    
+    TCPSocket *getClientSocket(int id) {
+        if (id >= HTTPD_MAX_CLIENTS) return NULL;
+        return _state[id].client;
+    }
+    int send (int id, const char *body, int len, const char *header = NULL);
+    int sendstr (int id, const char *buf);
+    int hprintf(int id, const char* format, ...);
+    int receive (int id, char *buf, int len);
+    int attach (const char *uri, const char *dir);
+    int attach (const char *uri, void (*funcCgi)(int id));
+    int base64encode (const char *input, int length, char *output, int len);
+    int urlencode (const char *str, char *buf, int len);
+    int urldecode (const char *str, char *buf, int len);
+
+    static HTTPD * getInstance() {
+        return _inst;
+    };
+
+private :
+    static HTTPD *_inst;
+    Thread *_daemon;
+    TCPServer _server;
+    
+    NetworkStack *m_ns;
+
+#ifdef HTTPD_ENABLE_CLOSER
+    struct STATE _state[HTTPD_MAX_CLIENTS + 1];
+#else
+    struct STATE _state[HTTPD_MAX_CLIENTS];
+#endif
+
+    struct HANDLER {
+        char *uri;
+        char *dir;
+        void (*funcCgi)(int id);
+    } _handler[HTTPD_MAX_HANDLES];
+
+    int _handler_count;
+
+    static void daemon ();
+    static void child (void const *arg);
+    static void closer (void const *arg);
+
+    // --- HTTPD_req.cpp ---
+    int httpdFile (int id, char *dir);
+    void recvData (int id, char c);
+    int parseRequest (int id);
+    int parseHeader (int id);
+    void reqContentLength (int id, const char *buf);
+    void reqConnection (int id, const char *buf);
+    void reqUpgrade (int id, const char *buf);
+    void reqWebSocketVersion (int id, const char *buf);
+    void reqWebSocketKey (int id, const char *buf);
+
+    // --- HTTPD_ws.cpp ---
+    void recvWS (int id, char c);
+    int parseWebsocket (int id);
+    int acceptWebsocket (int id);
+
+    // --- HTTPD_util.cpp ---
+    int getHandler (const char *uri);
+    char *mimetype (char *file);
+    int strnicmp (const char *p1, const char *p2, int n);
+    int from_hex (int ch);
+    int to_hex (int code);
+};
+
+#endif
diff -r ada7d903623d -r 14382459c8b7 HTTPD/HTTPD_req.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPD/HTTPD_req.cpp	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,332 @@
+/* Copyright (C) 2013 Hiroshi Suga, MIT License
+ *
+ * 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 "HTTPD.h"
+
+
+int HTTPD::httpdFile (int id, char *dir) {
+    FILE *fp;
+    int i, len;
+    char buf[HTTPD_BUF_SIZE];
+    char file[HTTPD_CMD_SIZE];
+
+    INFO("httpdFile %d %s", id, dir);
+
+    strcpy(file, dir);
+    strcat(file, _state[id].filename);
+    if (file[strlen(file) - 1] == '/') {
+        strcat(file, "index.html");
+    }
+    DBG("file: %s\r\n", file);
+
+    fp = fopen(file, "r");
+    if (fp) {
+        strcpy(buf, "HTTP/1.1 200 OK\r\n");
+        _state[id].client->send(buf, strlen(buf));
+        {
+            // file size
+            i = ftell(fp);
+            fseek(fp, 0, SEEK_END);
+            len = ftell(fp);
+            fseek(fp, i, SEEK_SET);
+        }
+
+        strcpy(buf, "Server: GSwifi httpd\r\n");
+        _state[id].client->send(buf, strlen(buf));
+        if (_state[id].keepalive) {
+            strcpy(buf, "Connection: Keep-Alive\r\n");
+        } else {
+            strcpy(buf, "Connection: close\r\n");
+        }
+        _state[id].client->send(buf, strlen(buf));
+        sprintf(buf, "Content-Type: %s\r\n", mimetype(file));
+        _state[id].client->send(buf, strlen(buf));
+        sprintf(buf, "Content-Length: %d\r\n\r\n", len);
+        _state[id].client->send(buf, strlen(buf));
+
+        for (;;) {
+            i = fread(buf, sizeof(char), sizeof(buf), fp);
+            if (i <= 0) break;
+            _state[id].client->send(buf, i);
+#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
+            if (feof(fp)) break;
+#endif
+        }
+        fclose(fp);
+        return 0;
+    }
+    
+    httpdError(id, 404);
+    return -1;
+}
+
+void HTTPD::httpdError (int id, int err) {
+    char buf[HTTPD_CMD_SIZE], msg[30];
+    
+    switch (err) {
+    case 400:
+        strcpy(msg, "Bad Request");
+        break;
+    case 403:
+        strcpy(msg, "Forbidden");
+        break;
+    case 404:
+        strcpy(msg, "Not Found");
+        break;
+    case 500:
+    default:
+        strcpy(msg, "Internal Server Error");
+        break;
+    }
+    DBG("httpd error: %d %d %s\r\n", id, err, msg);
+    
+    sprintf(buf, "HTTP/1.1 %d %s\r\n", err, msg);
+    _state[id].client->send(buf, strlen(buf));
+    strcpy(buf, "Content-Type: text/html\r\n\r\n");
+    _state[id].client->send(buf, strlen(buf));
+
+    sprintf(buf, "<html><head><title>%d %s</title></head>\r\n", err, msg);
+    _state[id].client->send(buf, strlen(buf));
+    sprintf(buf, "<body><h1>%s</h1></body></html>\r\n", msg);
+    _state[id].client->send(buf, strlen(buf));
+    wait_ms(100);
+    _state[id].client->close();
+    //WARN("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
+    //WARN("%s %s %d %d -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length, err);
+}
+
+void HTTPD::recvData (int id, char c) {
+
+    switch (_state[id].mode) {
+    case MODE_REQUEST:
+        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
+            _state[id].buf->flush();
+            _state[id].buf->queue(c);
+            _state[id].mode = MODE_REQSTR;
+        } else {
+            _state[id].buf->flush();
+        }
+        break;
+    case MODE_REQSTR:
+        switch (c) {
+        case 0:
+            break;
+        case 0x0a: // LF
+        case 0x0d: // CR
+            if (parseRequest(id)) {
+                _state[id].mode = MODE_REQSTR;
+            } else {
+                _state[id].mode = MODE_HEADER;
+            }
+            _state[id].enter = 0;
+            break;
+        default:
+            _state[id].buf->queue(c);
+            break;
+        }
+        break;
+    case MODE_HEADER:
+        switch (c) {
+        case 0:
+            break;
+        case 0x0a: // LF
+        case 0x0d: // CR
+            if (_state[id].buf->available() == 0) {
+                if ((_state[id].enter == 0x0d && c == 0x0a) || (_state[id].enter == 0x0a && c == 0x0a)) {
+                    _state[id].buf->flush();
+                    if (_state[id].websocket) {
+                        INFO("MODE_WEBSOCKET");
+                        acceptWebsocket(id);
+                        _state[id].mode = MODE_WEBSOCKET;
+                    } else
+                    if (_state[id].length) {
+                        INFO("MODE_BODY");
+                        _state[id].mode = MODE_BODY;
+                    } else {
+                        INFO("MODE_ENTER");
+                        _state[id].mode = MODE_ENTER;
+                    }
+                }
+                _state[id].enter = c;
+                _state[id].buf->flush();
+                break;
+            }
+
+            parseHeader(id);
+            _state[id].enter = 0;
+            break;
+        default:
+            _state[id].buf->queue(c);
+            _state[id].enter = 0;
+            break;
+        }
+        break;
+    case MODE_BODY:
+        _state[id].buf->queue(c);
+        if (_state[id].buf->available() >= _state[id].length) {
+            _state[id].mode = MODE_ENTER;
+        }
+        break;
+    case MODE_WEBSOCKET:
+    case MODE_WEBSOCKET_MASK:
+    case MODE_WEBSOCKET_BODY:
+        recvWS(id, c);
+        break;
+    }
+
+    if (_state[id].mode == MODE_ENTER) {
+        int i = getHandler(_state[id].uri);
+        printf("handler = %d, uri = %s\r\n", i, _state[id].uri);
+        if (i >= 0) { 
+            if (_handler[i].dir) {
+                // file
+                httpdFile(id, _handler[i].dir);
+            } else if (_handler[i].funcCgi) {
+                // cgi
+                _handler[i].funcCgi(id);
+                _state[id].keepalive = 0;
+            } else {
+                httpdError(id, 403);
+            }
+        } else {
+            httpdError(id, 404);
+        }
+
+        if (_state[id].keepalive) {
+            DBG("keepalive %d", _state[id].keepalive);
+            _state[id].keepalive--;
+        } else {
+            _state[id].client->close();
+        }
+        _state[id].mode = MODE_REQUEST;
+    } else
+    if (_state[id].mode == MODE_WEBSOCKET_ENTER) {
+        parseWebsocket(id);
+        _state[id].mode = MODE_WEBSOCKET;
+    }
+}
+
+int HTTPD::parseRequest (int id) {
+    int i, j, len;
+    char buf[HTTPD_CMD_SIZE];
+
+    for (len = 0; len < sizeof(buf); len++) {
+        if (_state[id].buf->dequeue(&buf[len]) == false) break;
+    }
+    buf[len] = 0;
+
+    if (strnicmp(buf, "GET ", 4) == 0) {
+        _state[id].req = REQ_HTTPGET;
+        j = 4;
+    } else
+    if (strnicmp(buf, "POST ", 5) == 0) {
+        _state[id].req = REQ_HTTPPOST;
+        j = 5;
+    } else {
+        return -1;
+    }
+
+    for (i = 0; i < len - j; i ++) {
+        _state[id].uri[i] = buf[i + j];
+        if (buf[i + j] == ' ' || i >= sizeof(buf) - 1) {
+            _state[id].uri[i] = 0;
+            INFO("URI %d '%s'", _state[id].req, _state[id].uri);
+            _state[id].mode = MODE_HEADER;
+            _state[id].buf->flush();
+            _state[id].length = 0;
+            _state[id].n = 0;
+            _state[id].websocket = 0;
+            _state[id].filename = NULL;
+            _state[id].querystring = NULL;
+            break;
+        }
+    }
+
+    i = getHandler(_state[id].uri);
+    if (i >= 0) { 
+        _state[id].filename = &_state[id].uri[strlen(_handler[i].uri)];
+        for (i = 0; i < strlen(_state[id].filename); i ++) {
+            if (_state[id].filename[i] == '?') {
+                _state[id].filename[i] = 0;
+                _state[id].querystring = _state[id].filename + i + 1;
+                break;
+            }
+        }
+        INFO("FILE '%s' QUERY '%s'", _state[id].filename, _state[id].querystring);
+    }
+    return 0;
+}
+
+#define HEADER_TABLE_NUM 5
+int HTTPD::parseHeader (int id) {
+    int i;
+    char buf[HTTPD_CMD_SIZE];
+    static const struct HEADER_TABLE {
+        const char header[24];
+        void (HTTPD::*func)(int id, const char*);
+    } header_table[HEADER_TABLE_NUM] = {
+      {"Content-Length:",         &HTTPD::reqContentLength},
+      {"Connection:",             &HTTPD::reqConnection},
+      {"Upgrade: websocket",      &HTTPD::reqUpgrade},
+      {"Sec-WebSocket-Version:",  &HTTPD::reqWebSocketVersion},
+      {"Sec-WebSocket-Key:",      &HTTPD::reqWebSocketKey},
+    };
+    for (i = 0; i < sizeof(buf); i++) {
+        if (_state[id].buf->dequeue(&buf[i]) == false) break;
+    }
+    buf[i] = 0;
+
+    for (i = 0; i < HEADER_TABLE_NUM; i ++) {
+        if (strnicmp(buf, header_table[i].header, strlen(header_table[i].header)) == 0) {
+            DBG("parse header %d '%s'\r\n", i, buf);
+            if (header_table[i].func != NULL) {
+                (this->*(header_table[i].func))(id, buf);
+            }
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+void HTTPD::reqContentLength (int id, const char *buf) {
+    _state[id].length = atoi(&buf[16]);
+}
+
+void HTTPD::reqConnection (int id, const char *buf) {
+    if (strnicmp(&buf[12], "Keep-Alive", 10) == 0 && _state[id].keepalive == 0) {
+        _state[id].keepalive = HTTPD_KEEPALIVE;
+    } else {
+        _state[id].keepalive = 0;
+    }
+}
+
+void HTTPD::reqUpgrade (int id, const char *buf) {
+    if (! _state[id].websocket) _state[id].websocket = 1;
+}
+
+void HTTPD::reqWebSocketVersion (int id, const char *buf) {
+    _state[id].websocket = atoi(&buf[23]);
+}
+
+void HTTPD::reqWebSocketKey (int id, const char *buf) {
+    if (_state[id].websocket_key == NULL) {
+        _state[id].websocket_key = (char*)malloc(30);
+    }
+    strncpy(_state[id].websocket_key, &buf[19], 30);
+}
diff -r ada7d903623d -r 14382459c8b7 HTTPD/HTTPD_util.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPD/HTTPD_util.cpp	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,256 @@
+/* Copyright (C) 2013 Hiroshi Suga, MIT License
+ *
+ * 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 "HTTPD.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MIMETABLE_NUM 9
+static const struct {
+    char ext[5];
+    char type[24];
+} mimetable[MIMETABLE_NUM] = {
+    {"txt", "text/plain"},  // default
+    {"html", "text/html"},
+    {"htm", "text/html"},
+    {"css", "text/css"},
+    {"js",  "application/javascript"},
+    {"jpg", "image/jpeg"},
+    {"png", "image/png"},
+    {"gif", "image/gif"},
+    {"ico", "image/x-icon"},
+};
+
+char *HTTPD::getUri (int id) {
+    return _state[id].uri;
+}
+
+char *HTTPD::getFilename (int id) {
+    return _state[id].filename;
+}
+
+char *HTTPD::getQueryString (int id) {
+    return _state[id].querystring;
+}
+
+int HTTPD::receive (int id, char *buf, int len) {
+    int i;
+
+    for (i = 0; i < len; i ++) {
+        if (_state[id].buf->dequeue(&buf[i]) == false) break;
+    }
+    return i;
+}
+
+int HTTPD::send (int id, const char *body, int len, const char *header) {
+    char buf[HTTPD_CMD_SIZE];
+
+    strcpy(buf, "HTTP/1.1 200 OK\r\n");
+    _state[id].client->send(buf, strlen(buf));
+    strcpy(buf, "Server: GSwifi httpd\r\n");
+    _state[id].client->send(buf, strlen(buf));
+    if (_state[id].keepalive) {
+        strcpy(buf, "Connection: Keep-Alive\r\n");
+    } else {
+        strcpy(buf, "Connection: close\r\n");
+    }
+    _state[id].client->send(buf, strlen(buf));
+    if (header) {
+        _state[id].client->send((char*)header, strlen(header));
+    }
+    sprintf(buf, "Content-Length: %d\r\n\r\n", len);
+    _state[id].client->send(buf, strlen(buf));
+
+    return _state[id].client->send((char*)body, len);
+}
+
+int HTTPD::sendstr (int id, const char *buf) {
+    return _state[id].client->send((char*)buf, strlen(buf));
+}
+
+
+int HTTPD::hprintf(int id, const char* format, ...) {
+    //FIX ME: This could result in memory overruns if the buffer size is exceeded
+    static Mutex _mtx;
+    static char  _buf[1024];
+
+    std::va_list arg;
+    
+    _mtx.lock();
+    
+    va_start(arg, format);
+    vsprintf(_buf, format, arg);
+    va_end(arg);
+
+    int r = _state[id].client->send(_buf, strlen(_buf));
+
+    _mtx.unlock();
+    return r;
+}
+
+int HTTPD::getHandler (const char *uri) {
+    int i;
+
+    for (i = 0; i < _handler_count; i ++) {
+        if (strncmp(uri, _handler[i].uri, strlen(_handler[i].uri)) == NULL) {
+            // found
+            return i;
+        }
+    }
+    return -1;
+}
+
+int HTTPD::attach (const char *uri, const char *dir) {
+    if (_handler_count < HTTPD_MAX_HANDLES) {
+        _handler[_handler_count].uri = (char*)malloc(strlen(uri) + 1);
+        strcpy(_handler[_handler_count].uri, uri);
+        _handler[_handler_count].dir = (char*)malloc(strlen(dir) + 1);
+        strcpy(_handler[_handler_count].dir, dir);
+        _handler[_handler_count].funcCgi = NULL;
+        _handler_count ++;
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+int HTTPD::attach (const char *uri, void (*funcCgi)(int)) {
+    if (_handler_count < HTTPD_MAX_HANDLES) {
+        _handler[_handler_count].uri = (char*)malloc(strlen(uri) + 1);
+        strcpy(_handler[_handler_count].uri, uri);
+        _handler[_handler_count].dir = NULL;
+        _handler[_handler_count].funcCgi = funcCgi;
+        _handler_count ++;
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+
+char *HTTPD::mimetype (char *file) {
+    int i, j;
+
+    for (i = 0; i < MIMETABLE_NUM; i ++) {
+        j = strlen(mimetable[i].ext);
+        if (file[strlen(file) - j - 1] == '.' &&
+          strnicmp(&file[strlen(file) - j], mimetable[i].ext, j) == NULL) {
+            return (char*)mimetable[i].type;
+        }
+    }
+    return (char*)mimetable[0].type;
+}
+
+int HTTPD::strnicmp (const char *p1, const char *p2, int n) {
+    int i, r = -1;
+    char c1, c2;
+    
+    for (i = 0; i < n; i ++) {
+        c1 = (p1[i] >= 'a' && p1[i] <= 'z') ? p1[i] - ('a' - 'A'): p1[i];
+        c2 = (p2[i] >= 'a' && p2[i] <= 'z') ? p2[i] - ('a' - 'A'): p2[i];
+        r = c1 - c2;
+        if (r) break;
+    }
+    return r;
+}
+
+
+/* base64encode code from 
+ * Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ */
+int HTTPD::base64encode (const char *input, int length, char *output, int len) {
+    static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    unsigned int c, c1, c2, c3;
+ 
+    if (len < ((((length-1)/3)+1)<<2)) return -1;
+    for(unsigned int i = 0, j = 0; i<length; i+=3,j+=4) {
+        c1 = ((((unsigned char)*((unsigned char *)&input[i]))));
+        c2 = (length>i+1)?((((unsigned char)*((unsigned char *)&input[i+1])))):0;
+        c3 = (length>i+2)?((((unsigned char)*((unsigned char *)&input[i+2])))):0;
+ 
+        c = ((c1 & 0xFC) >> 2);
+        output[j+0] = base64[c];
+        c = ((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4);
+        output[j+1] = base64[c];
+        c = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6);
+        output[j+2] = (length>i+1)?base64[c]:'=';
+        c = (c3 & 0x3F);
+        output[j+3] = (length>i+2)?base64[c]:'=';
+    }
+    output[(((length-1)/3)+1)<<2] = '\0';
+    return 0;
+}
+ 
+ 
+/* urlencode code from 
+ * Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ */
+int HTTPD::urlencode (const char *str, char *buf, int len) {
+//  char *pstr = str, *buf = (char*)malloc(strlen(str) * 3 + 1), *pbuf = buf;
+    const char *pstr = str;
+    char *pbuf = buf;
+ 
+    if (len < (strlen(str) * 3 + 1)) return -1;
+    while (*pstr) {
+        if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') 
+            *pbuf++ = *pstr;
+        else if (*pstr == ' ') 
+            *pbuf++ = '+';
+        else 
+            *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
+        pstr++;
+    }
+    *pbuf = '\0';
+    return 0;
+}
+ 
+/* urldecode code from 
+ * Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ */
+int HTTPD::urldecode (const char *str, char *buf, int len) {
+//  char *pstr = str, *buf = (char*)malloc(strlen(str) + 1), *pbuf = buf;
+    const char *pstr = str;
+    char *pbuf = buf;
+ 
+    if (len < (strlen(str) / 3 - 1)) return -1;
+    while (*pstr) {
+        if (*pstr == '%') {
+            if (pstr[1] && pstr[2]) {
+                *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
+                pstr += 2;
+            }
+        } else if (*pstr == '+') { 
+            *pbuf++ = ' ';
+        } else {
+            *pbuf++ = *pstr;
+        }
+        pstr++;
+    }
+    *pbuf = '\0';
+    return 0;
+}
+
+int HTTPD::from_hex (int ch) {
+  return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
+}
+ 
+int HTTPD::to_hex (int code) {
+  static char hex[] = "0123456789abcdef";
+  return hex[code & 15];
+}
diff -r ada7d903623d -r 14382459c8b7 HTTPD/HTTPD_ws.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPD/HTTPD_ws.cpp	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,198 @@
+/* Copyright (C) 2013 Hiroshi Suga, MIT License
+ *
+ * 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 "HTTPD.h"
+#include "sha1.h"
+
+void HTTPD::recvWS (int id, char c) {
+
+    switch (_state[id].mode) {
+    case MODE_WEBSOCKET:
+        if (_state[id].n == 0) {
+            // flag
+            _state[id].websocket_opcode = c & 0x0f;
+            _state[id].websocket_flg = c << 8;
+            _state[id].n ++;
+        } else
+        if (_state[id].n == 1) {
+            // length 7bit
+            _state[id].websocket_flg |= c;
+            _state[id].length = c & 0x7f;
+            _state[id].n ++;
+            if (_state[id].length < 126) {
+                _state[id].n = 0;
+                if (_state[id].length) {
+                    if (_state[id].websocket_flg & 0x0080) {
+                        _state[id].mode = MODE_WEBSOCKET_MASK;
+                    } else {
+                        _state[id].mode = MODE_WEBSOCKET_BODY;
+                    }
+                } else {
+                    _state[id].mode = MODE_WEBSOCKET_ENTER;
+                }
+                DBG("ws length %d\r\n", _state[id].length);
+            }
+        } else {
+            // length 16bit,64bit
+            if (_state[id].n == 2) {
+                _state[id].length = c;
+                _state[id].n ++;
+            } else
+            if (_state[id].n < 9 && (_state[id].websocket_flg & 0x7f) == 127) {
+                // 64bit
+                _state[id].length = (_state[id].length << 8) | c;
+                _state[id].n ++;
+            } else {
+                // end
+                _state[id].length = (_state[id].length << 8) | c;
+                _state[id].n = 0;
+                if (_state[id].websocket_flg & 0x0080) {
+                    _state[id].mode = MODE_WEBSOCKET_MASK;
+                } else {
+                    _state[id].mode = MODE_WEBSOCKET_BODY;
+                }
+                DBG("ws length2 %d\r\n", _state[id].length);
+            }
+        }
+        break;
+    case MODE_WEBSOCKET_MASK:
+        // masking key
+        _state[id].websocket_mask[_state[id].n] = c;
+        _state[id].n ++;
+        if (_state[id].n >= 4) {
+            _state[id].n = 0;
+            _state[id].mode = MODE_WEBSOCKET_BODY;
+            DBG("ws mask\r\n");
+        }
+        break;
+    case MODE_WEBSOCKET_BODY:
+        // payload
+        if (_state[id].websocket_flg & 0x0080) {
+            // un-mask
+            _state[id].buf->queue(c ^ _state[id].websocket_mask[_state[id].n & 0x03]); 
+        } else {
+            _state[id].buf->queue(c); 
+        }
+        _state[id].n ++;
+        if (_state[id].n >= _state[id].length) {
+            _state[id].mode = MODE_WEBSOCKET_ENTER;
+        }
+        break;
+    }
+}
+
+int HTTPD::parseWebsocket (int id) {
+    int i;
+
+    DBG("ws opcode %d\r\n", _state[id].websocket_opcode);
+    switch (_state[id].websocket_opcode) {
+    case 0x00: // continuation
+        break;
+    case 0x01: // text
+    case 0x02: // binary
+        i = getHandler(_state[id].uri);
+        if (i >= 0) {
+            if (_handler[i].funcCgi) {
+                // cgi
+                _handler[i].funcCgi(id);
+            }
+        }
+        break;
+    case 0x08: // close
+        _state[id].client->close();
+        break;
+    case 0x09: // ping
+        {
+        char pong[_state[id].n + 2];
+        pong[0] = 0x8a;
+        pong[1] = 0x04;
+        for (i = 0; i < _state[id].length; i ++) {
+            if (_state[id].buf->dequeue(&pong[i + 2]) == false) break;
+        }
+        _state[id].client->send(pong, _state[id].length + 2);
+        }
+        break;
+    case 0x0a: // pong
+        break;
+    default:
+        break;
+    }
+    _state[id].n = 0;
+    _state[id].length = 0;
+    return 0;
+}
+
+int HTTPD::acceptWebsocket (int id) {
+    char buf[HTTPD_CMD_SIZE], buf2[HTTPD_CMD_SIZE];
+    
+    DBG("websocket accept: %d\r\n", id);
+
+    strcpy(buf, "HTTP/1.1 101 Switching Protocols\r\n");
+    _state[id].client->send(buf, strlen(buf));
+    strcpy(buf, "Upgrade: websocket\r\n");
+    _state[id].client->send(buf, strlen(buf));
+    strcpy(buf, "Connection: Upgrade\r\n");
+    _state[id].client->send(buf, strlen(buf));
+
+    strcpy(buf, "Sec-WebSocket-Accept: ");
+    _state[id].client->send(buf, strlen(buf));
+    strcpy(buf, _state[id].websocket_key);
+    strcat(buf, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+    sha1(buf, strlen(buf), buf2);
+    base64encode(buf2, 20, buf, sizeof(buf));
+    _state[id].client->send(buf, strlen(buf));
+    strcpy(buf, "\r\n\r\n");
+    _state[id].client->send(buf, strlen(buf));
+    //_state[id].client->set_blocking(true, HTTPD_TIMEOUT * 100);
+    return 0;
+}
+
+int HTTPD::sendWebsocket (int id, const char *buf, int len, const char *mask) {
+    HTTPD *httpd = HTTPD::getInstance();
+    int i = 0, r;
+    char tmp[10];
+
+    tmp[i++] = 0x81; // single, text frame
+    if (len < 126) {
+        tmp[i++] = (mask == NULL ? 0 : 0x80) | len;
+    } else {
+        tmp[i++] = (mask == NULL ? 0 : 0x80) | 126;
+        tmp[i++] = (len >> 8) & 0xff;
+        tmp[i++] = len & 0xff;
+    }
+    if (mask) {
+        memcpy(&tmp[i], mask, 4);
+        i += 4;
+    }
+    r = httpd->_state[id].client->send(tmp, i);
+
+    if (r >= 0) {
+        if (mask) {
+            char tmp2[len];
+            for (i = 0; i < len; i ++) {
+                tmp2[i] = buf[i] ^ mask[i & 0x03];
+            }
+            r = httpd->_state[id].client->send(tmp2, len);
+        } else {
+            r = httpd->_state[id].client->send((char*)buf, len);
+        }
+    }
+    return r;
+}
+
+
diff -r ada7d903623d -r 14382459c8b7 HTTPD/sha1.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPD/sha1.cpp	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,364 @@
+/*
+ * source from http://www.ipa.go.jp/security/rfc/RFC3174JA.html
+ */
+#include "sha1.h"
+
+/*
+ *  Define the SHA1 circular left shift macro
+ */
+#define SHA1CircularShift(bits,word) \
+                (((word) << (bits)) | ((word) >> (32-(bits))))
+
+/* Local Function Prototyptes */
+void SHA1PadMessage(SHA1Context *);
+void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*
+ *  SHA1Reset
+ *
+ *  Description:
+ *      This function will initialize the SHA1Context in preparation
+ *      for computing a new SHA1 message digest.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to reset.
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+int SHA1Reset(SHA1Context *context)
+{
+    if (!context)
+    {
+        return shaNull;
+    }
+
+    context->Length_Low             = 0;
+    context->Length_High            = 0;
+    context->Message_Block_Index    = 0;
+
+    context->Intermediate_Hash[0]   = 0x67452301;
+    context->Intermediate_Hash[1]   = 0xEFCDAB89;
+    context->Intermediate_Hash[2]   = 0x98BADCFE;
+    context->Intermediate_Hash[3]   = 0x10325476;
+    context->Intermediate_Hash[4]   = 0xC3D2E1F0;
+
+    context->Computed   = 0;
+    context->Corrupted  = 0;
+
+    return shaSuccess;
+}
+
+/*
+ *  SHA1Result
+ *
+ *  Description:
+ *      This function will return the 160-bit message digest into the
+ *      Message_Digest array  provided by the caller.
+ *      NOTE: The first octet of hash is stored in the 0th element,
+ *            the last octet of hash in the 19th element.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to use to calculate the SHA-1 hash.
+ *      Message_Digest: [out]
+ *          Where the digest is returned.
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+int SHA1Result( SHA1Context *context,
+                uint8_t Message_Digest[SHA1HashSize])
+{
+    int i;
+
+    if (!context || !Message_Digest)
+    {
+        return shaNull;
+    }
+
+    if (context->Corrupted)
+    {
+        return context->Corrupted;
+    }
+
+    if (!context->Computed)
+    {
+        SHA1PadMessage(context);
+        for(i=0; i<64; ++i)
+        {
+            /* message may be sensitive, clear it out */
+            context->Message_Block[i] = 0;
+        }
+        context->Length_Low = 0;    /* and clear length */
+        context->Length_High = 0;
+        context->Computed = 1;
+    }
+
+    for(i = 0; i < SHA1HashSize; ++i)
+    {
+        Message_Digest[i] = context->Intermediate_Hash[i>>2]
+                            >> 8 * ( 3 - ( i & 0x03 ) );
+    }
+
+    return shaSuccess;
+}
+
+/*
+ *  SHA1Input
+ *
+ *  Description:
+ *      This function accepts an array of octets as the next portion
+ *      of the message.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The SHA context to update
+ *      message_array: [in]
+ *          An array of characters representing the next portion of
+ *          the message.
+ *      length: [in]
+ *          The length of the message in message_array
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+int SHA1Input(    SHA1Context    *context,
+                  const uint8_t  *message_array,
+                  unsigned       length)
+{
+    if (!length)
+    {
+        return shaSuccess;
+    }
+
+    if (!context || !message_array)
+    {
+        return shaNull;
+    }
+
+    if (context->Computed)
+    {
+        context->Corrupted = shaStateError;
+        return shaStateError;
+    }
+
+    if (context->Corrupted)
+    {
+         return context->Corrupted;
+    }
+    while(length-- && !context->Corrupted)
+    {
+    context->Message_Block[context->Message_Block_Index++] =
+                    (*message_array & 0xFF);
+
+    context->Length_Low += 8;
+    if (context->Length_Low == 0)
+    {
+        context->Length_High++;
+        if (context->Length_High == 0)
+        {
+            /* Message is too long */
+            context->Corrupted = 1;
+        }
+    }
+
+    if (context->Message_Block_Index == 64)
+    {
+        SHA1ProcessMessageBlock(context);
+    }
+
+    message_array++;
+    }
+
+    return shaSuccess;
+}
+
+/*
+ *  SHA1ProcessMessageBlock
+ *
+ *  Description:
+ *      This function will process the next 512 bits of the message
+ *      stored in the Message_Block array.
+ *
+ *  Parameters:
+ *      None.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *      Many of the variable names in this code, especially the
+ *      single character names, were used because those were the
+ *      names used in the publication.
+ *
+ *
+ */
+void SHA1ProcessMessageBlock(SHA1Context *context)
+{
+    const uint32_t K[] =    {       /* Constants defined in SHA-1   */
+                            0x5A827999,
+                            0x6ED9EBA1,
+                            0x8F1BBCDC,
+                            0xCA62C1D6
+                            };
+    int           t;                 /* Loop counter                */
+    uint32_t      temp;              /* Temporary word value        */
+    uint32_t      W[80];             /* Word sequence               */
+    uint32_t      A, B, C, D, E;     /* Word buffers                */
+
+    /*
+     *  Initialize the first 16 words in the array W
+     */
+    for(t = 0; t < 16; t++)
+    {
+        W[t] = context->Message_Block[t * 4] << 24;
+        W[t] |= context->Message_Block[t * 4 + 1] << 16;
+        W[t] |= context->Message_Block[t * 4 + 2] << 8;
+        W[t] |= context->Message_Block[t * 4 + 3];
+    }
+
+    for(t = 16; t < 80; t++)
+    {
+       W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+    }
+
+    A = context->Intermediate_Hash[0];
+    B = context->Intermediate_Hash[1];
+    C = context->Intermediate_Hash[2];
+    D = context->Intermediate_Hash[3];
+    E = context->Intermediate_Hash[4];
+
+    for(t = 0; t < 20; t++)
+    {
+        temp =  SHA1CircularShift(5,A) +
+                ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 20; t < 40; t++)
+    {
+        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 40; t < 60; t++)
+    {
+        temp = SHA1CircularShift(5,A) +
+               ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 60; t < 80; t++)
+    {
+        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    context->Intermediate_Hash[0] += A;
+    context->Intermediate_Hash[1] += B;
+    context->Intermediate_Hash[2] += C;
+    context->Intermediate_Hash[3] += D;
+    context->Intermediate_Hash[4] += E;
+
+    context->Message_Block_Index = 0;
+}
+
+
+/*
+ *  SHA1PadMessage
+ *
+ *  Description:
+ *      According to the standard, the message must be padded to an even
+ *      512 bits.  The first padding bit must be a '1'.  The last 64
+ *      bits represent the length of the original message.  All bits in
+ *      between should be 0.  This function will pad the message
+ *      according to those rules by filling the Message_Block array
+ *      accordingly.  It will also call the ProcessMessageBlock function
+ *      provided appropriately.  When it returns, it can be assumed that
+ *      the message digest has been computed.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to pad
+ *      ProcessMessageBlock: [in]
+ *          The appropriate SHA*ProcessMessageBlock function
+ *  Returns:
+ *      Nothing.
+ *
+ */
+
+void SHA1PadMessage(SHA1Context *context)
+{
+    /*
+     *  Check to see if the current message block is too small to hold
+     *  the initial padding bits and length.  If so, we will pad the
+     *  block, process it, and then continue padding into a second
+     *  block.
+     */
+    if (context->Message_Block_Index > 55)
+    {
+        context->Message_Block[context->Message_Block_Index++] = 0x80;
+        while(context->Message_Block_Index < 64)
+        {
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+
+        SHA1ProcessMessageBlock(context);
+
+        while(context->Message_Block_Index < 56)
+        {
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+    }
+    else
+    {
+        context->Message_Block[context->Message_Block_Index++] = 0x80;
+        while(context->Message_Block_Index < 56)
+        {
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+    }
+
+    /*
+     *  Store the message length as the last 8 octets
+     */
+    context->Message_Block[56] = context->Length_High >> 24;
+    context->Message_Block[57] = context->Length_High >> 16;
+    context->Message_Block[58] = context->Length_High >> 8;
+    context->Message_Block[59] = context->Length_High;
+    context->Message_Block[60] = context->Length_Low >> 24;
+    context->Message_Block[61] = context->Length_Low >> 16;
+    context->Message_Block[62] = context->Length_Low >> 8;
+    context->Message_Block[63] = context->Length_Low;
+
+    SHA1ProcessMessageBlock(context);
+}
+
+void sha1 (const char *input, int len, char *output) {
+     SHA1Context sha;
+ 
+    SHA1Reset(&sha);
+    SHA1Input(&sha, (unsigned char*)input, len);
+    SHA1Result(&sha, (uint8_t*)output);
+ }
diff -r ada7d903623d -r 14382459c8b7 HTTPD/sha1.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPD/sha1.h	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,78 @@
+/*
+ * source from http://www.ipa.go.jp/security/rfc/RFC3174JA.html
+ */
+/*
+ *  sha1.h
+ *
+ *  Description:
+ *      This is the header file for code which implements the Secure
+ *      Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
+ *      April 17, 1995.
+ *
+ *      Many of the variable names in this code, especially the
+ *      single character names, were used because those were the names
+ *      used in the publication.
+ *
+ *      Please read the file sha1.c for more information.
+ *
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+#include "mbed.h"
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typdef the following:
+ *    name              meaning
+ *  uint32_t         unsigned 32 bit integer
+ *  uint8_t          unsigned 8 bit integer (i.e., unsigned char)
+ *  int_least16_t    integer of >= 16 bits
+ *
+ */
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+enum
+{
+    shaSuccess = 0,
+    shaNull,            /* Null pointer parameter */
+    shaInputTooLong,    /* input data too long */
+    shaStateError       /* called Input after Result */
+};
+#endif
+#define SHA1HashSize 20
+
+/*
+ *  This structure will hold context information for the SHA-1
+ *  hashing operation
+ */
+typedef struct SHA1Context
+{
+    uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */
+
+    uint32_t Length_Low;            /* Message length in bits      */
+    uint32_t Length_High;           /* Message length in bits      */
+
+                               /* Index into message block array   */
+    int_least16_t Message_Block_Index;
+    uint8_t Message_Block[64];      /* 512-bit message blocks      */
+
+    int Computed;               /* Is the digest computed?         */
+    int Corrupted;             /* Is the message digest corrupted? */
+} SHA1Context;
+
+/*
+ *  Function Prototypes
+ */
+
+int SHA1Reset(  SHA1Context *);
+int SHA1Input(  SHA1Context *,
+                const uint8_t *,
+                unsigned int);
+int SHA1Result( SHA1Context *,
+                uint8_t Message_Digest[SHA1HashSize]);
+
+
+void sha1 (const char *input, int len, char *output);
+#endif
diff -r ada7d903623d -r 14382459c8b7 SNTPClient.lib
--- a/SNTPClient.lib	Thu Jun 15 20:25:40 2017 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-https://developer.mbed.org/teams/AMETEK-Powervar/code/SNTPClient/#a5a91530712d
diff -r ada7d903623d -r 14382459c8b7 SNTPClient/SNTPClient.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SNTPClient/SNTPClient.cpp	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,447 @@
+#include "SNTPClient.h"
+#include "DNSClient.h"
+
+#define MAX_TRY_WRITE 20
+#define MAX_TRY_READ 10
+
+//Debug is disabled by default
+#ifdef _SNTP_DEBUG_
+#define DBG(x, ...) std::printf("[SNTPClient : DBG]"x"\r\n", ##__VA_ARGS__); 
+#define WARN(x, ...) std::printf("[SNTPClient : WARN]"x"\r\n", ##__VA_ARGS__); 
+#define ERR(x, ...) std::printf("[SNTPClient : ERR]"x"\r\n", ##__VA_ARGS__); 
+#else
+#define DBG(x, ...) 
+#define WARN(x, ...)
+#define ERR(x, ...) 
+#endif
+
+#define INFO(x, ...) printf("[SNTPClient : INFO]"x"\r\n", ##__VA_ARGS__); 
+
+SNTPClient::SNTPClient(NetworkStack *ns, const char* url, uint8_t time_zone) {
+    DNSClient dns(ns);
+    
+    if (dns.lookup(url)) {
+        const char* dns_addr = dns.get_ip_address();
+        memcpy(host, dns_addr, strlen(dns_addr));
+        host[strlen(dns_addr)] = '\0';
+    } else {
+        memcpy(host, url, strlen(url));
+        host[strlen(url)] = '\0';
+    }
+    
+    port = ntp_port;
+    m_ns = ns;
+    m_udp = NULL;
+    tz = time_zone;
+}
+
+bool SNTPClient::connect() {
+    if (m_udp == NULL) {
+        m_udp = new UDPSocket;
+        m_udp->open(m_ns);
+    }
+    
+    m_udp->set_blocking(false);
+    m_udp->set_timeout(3000);
+    m_udp->bind(rand()&0x7fff);
+
+    sntp_server.set_ip_address(host);
+    sntp_server.set_port(port);
+
+    nsapi_addr_t na = sntp_server.get_addr();
+    NTPformat.dstaddr[0] = na.bytes[0];
+    NTPformat.dstaddr[1] = na.bytes[1];
+    NTPformat.dstaddr[2] = na.bytes[2];
+    NTPformat.dstaddr[3] = na.bytes[3];
+    DBG("NTP Server: %s\r\n", sntp_server.get_ip_address());
+
+    uint8_t Flag;
+    NTPformat.leap = 0;           /* leap indicator */
+    NTPformat.version = 4;        /* version number */
+    NTPformat.mode = 3;           /* mode */
+    NTPformat.stratum = 0;        /* stratum */
+    NTPformat.poll = 0;           /* poll interval */
+    NTPformat.precision = 0;      /* precision */
+    NTPformat.rootdelay = 0;      /* root delay */
+    NTPformat.rootdisp = 0;       /* root dispersion */
+    NTPformat.refid = 0;          /* reference ID */
+    NTPformat.reftime = 0;        /* reference time */
+    NTPformat.org = 0;            /* origin timestamp */
+    NTPformat.rec = 0;            /* receive timestamp */
+    NTPformat.xmt = 1;            /* transmit timestamp */
+
+    Flag = (NTPformat.leap<<6)+(NTPformat.version<<3)+NTPformat.mode; //one byte Flag
+    memcpy(ntpmessage,(void const*)(&Flag),1);
+
+    return true;
+}
+
+bool SNTPClient::getTime(datetime *time) {
+    if (!m_udp) return false;
+    
+    uint16_t startindex = 40; //last 8-byte of data_buf[size is 48 byte] is xmt, so the startindex should be 40
+
+    int n = m_udp->sendto(sntp_server, (char *)ntpmessage, sizeof(ntpmessage));
+    
+    char in_buffer[MAX_SNTP_BUF_SIZE];
+    n = m_udp->recvfrom(&sntp_server, in_buffer, sizeof(in_buffer));
+
+    if(n <= 0) {
+        return false;
+    }
+
+    get_seconds_from_ntp_server((uint8_t *)in_buffer,startindex);
+
+    time->yy = Nowdatetime.yy;
+    time->mo = Nowdatetime.mo;
+    time->dd = Nowdatetime.dd;
+    time->hh = Nowdatetime.hh;
+    time->mm = Nowdatetime.mm;
+    time->ss = Nowdatetime.ss;
+
+    return true;
+}
+
+bool SNTPClient::close() {
+    if (m_udp) {
+        delete m_udp;
+        m_udp = NULL;
+    }
+    return true;
+}
+
+char* SNTPClient::getHost() {
+    return host;
+}
+
+/*
+00)UTC-12:00 Baker Island, Howland Island (both uninhabited)
+01) UTC-11:00 American Samoa, Samoa
+02) UTC-10:00 (Summer)French Polynesia (most), United States (Aleutian Islands, Hawaii)
+03) UTC-09:30 Marquesas Islands
+04) UTC-09:00 Gambier Islands;(Summer)United States (most of Alaska)
+05) UTC-08:00 (Summer)Canada (most of British Columbia), Mexico (Baja California)
+06) UTC-08:00 United States (California, most of Nevada, most of Oregon, Washington (state))
+07) UTC-07:00 Mexico (Sonora), United States (Arizona); (Summer)Canada (Alberta)
+08) UTC-07:00 Mexico (Chihuahua), United States (Colorado)
+09) UTC-06:00 Costa Rica, El Salvador, Ecuador (Galapagos Islands), Guatemala, Honduras
+10) UTC-06:00 Mexico (most), Nicaragua;(Summer)Canada (Manitoba, Saskatchewan), United States (Illinois, most of Texas)
+11) UTC-05:00 Colombia, Cuba, Ecuador (continental), Haiti, Jamaica, Panama, Peru
+12) UTC-05:00 (Summer)Canada (most of Ontario, most of Quebec)
+13) UTC-05:00 United States (most of Florida, Georgia, Massachusetts, most of Michigan, New York, North Carolina, Ohio, Washington D.C.)
+14) UTC-04:30 Venezuela
+15) UTC-04:00 Bolivia, Brazil (Amazonas), Chile (continental), Dominican Republic, Canada (Nova Scotia), Paraguay,
+16) UTC-04:00 Puerto Rico, Trinidad and Tobago
+17) UTC-03:30 Canada (Newfoundland)
+18) UTC-03:00 Argentina; (Summer) Brazil (Brasilia, Rio de Janeiro, Sao Paulo), most of Greenland, Uruguay
+19) UTC-02:00 Brazil (Fernando de Noronha), South Georgia and the South Sandwich Islands
+20) UTC-01:00 Portugal (Azores), Cape Verde
+21) UTC&#177;00:00 Cote d'Ivoire, Faroe Islands, Ghana, Iceland, Senegal; (Summer) Ireland, Portugal (continental and Madeira)
+22) UTC&#177;00:00 Spain (Canary Islands), Morocco, United Kingdom
+23) UTC+01:00 Angola, Cameroon, Nigeria, Tunisia; (Summer)Albania, Algeria, Austria, Belgium, Bosnia and Herzegovina,
+24) UTC+01:00 Spain (continental), Croatia, Czech Republic, Denmark, Germany, Hungary, Italy, Kinshasa, Kosovo,
+25) UTC+01:00 Macedonia, France (metropolitan), the Netherlands, Norway, Poland, Serbia, Slovakia, Slovenia, Sweden, Switzerland
+26) UTC+02:00 Libya, Egypt, Malawi, Mozambique, South Africa, Zambia, Zimbabwe, (Summer)Bulgaria, Cyprus, Estonia,
+27) UTC+02:00 Finland, Greece, Israel, Jordan, Latvia, Lebanon, Lithuania, Moldova, Palestine, Romania, Syria, Turkey, Ukraine
+28) UTC+03:00 Belarus, Djibouti, Eritrea, Ethiopia, Iraq, Kenya, Madagascar, Russia (Kaliningrad Oblast), Saudi Arabia,
+29) UTC+03:00 South Sudan, Sudan, Somalia, South Sudan, Tanzania, Uganda, Yemen
+30) UTC+03:30 (Summer)Iran
+31) UTC+04:00 Armenia, Azerbaijan, Georgia, Mauritius, Oman, Russia (European), Seychelles, United Arab Emirates
+32) UTC+04:30 Afghanistan
+33) UTC+05:00 Kazakhstan (West), Maldives, Pakistan, Uzbekistan
+34) UTC+05:30 India, Sri Lanka
+35) UTC+05:45 Nepal
+36) UTC+06:00 Kazakhstan (most), Bangladesh, Russia (Ural: Sverdlovsk Oblast, Chelyabinsk Oblast)
+37) UTC+06:30 Cocos Islands, Myanmar
+38) UTC+07:00 Jakarta, Russia (Novosibirsk Oblast), Thailand, Vietnam
+39) UTC+08:00 China, Hong Kong, Russia (Krasnoyarsk Krai), Malaysia, Philippines, Singapore, Taiwan, most of Mongolia, Western Australia
+40) UTC+09:00 Korea, East Timor, Russia (Irkutsk Oblast), Japan
+41) UTC+09:30 Australia (Northern Territory);(Summer)Australia (South Australia))
+42) UTC+10:00 Russia (Zabaykalsky Krai); (Summer)Australia (New South Wales, Queensland, Tasmania, Victoria)
+43) UTC+10:30 Lord Howe Island
+44) UTC+11:00 New Caledonia, Russia (Primorsky Krai), Solomon Islands
+45) UTC+11:30 Norfolk Island
+46) UTC+12:00 Fiji, Russia (Kamchatka Krai);(Summer)New Zealand
+47) UTC+12:45 (Summer)New Zealand
+48) UTC+13:00 Tonga
+49) UTC+14:00 Kiribati (Line Islands)
+*/
+void SNTPClient::get_seconds_from_ntp_server(uint8_t *buf, uint16_t idx)
+{
+    tstamp seconds = 0;
+    uint8_t i=0;
+    for (i = 0; i < 4; i++)
+    {
+        seconds = (seconds << 8) | buf[idx + i];
+    }
+    switch (tz) // Time Zone
+    {
+    case 0:
+        seconds -=  12*3600;
+        break;
+    case 1:
+        seconds -=  11*3600;
+        break;
+    case 2:
+        seconds -=  10*3600;
+        break;
+    case 3:
+        seconds -=  (9*3600+30*60);
+        break;
+    case 4:
+        seconds -=  9*3600;
+        break;
+    case 5:
+    case 6:
+        seconds -=  8*3600;
+        break;
+    case 7:
+    case 8:
+        seconds -=  7*3600;
+        break;
+    case 9:
+    case 10:
+        seconds -=  6*3600;
+        break;
+    case 11:
+    case 12:
+    case 13:
+        seconds -= 5*3600;
+        break;
+    case 14:
+        seconds -=  (4*3600+30*60);
+        break;
+    case 15:
+    case 16:
+        seconds -=  4*3600;
+        break;
+    case 17:
+        seconds -=  (3*3600+30*60);
+        break;
+    case 18:
+        seconds -=  3*3600;
+        break;
+    case 19:
+        seconds -=  2*3600;
+        break;
+    case 20:
+        seconds -=  1*3600;
+        break;
+    case 21:                            //?
+    case 22:
+        break;
+    case 23:
+    case 24:
+    case 25:
+        seconds +=  1*3600;
+        break;
+    case 26:
+    case 27:
+        seconds +=  2*3600;
+        break;
+    case 28:
+    case 29:
+        seconds +=  3*3600;
+        break;
+    case 30:
+        seconds +=  (3*3600+30*60);
+        break;
+    case 31:
+        seconds +=  4*3600;
+        break;
+    case 32:
+        seconds +=  (4*3600+30*60);
+        break;
+    case 33:
+        seconds +=  5*3600;
+        break;
+    case 34:
+        seconds +=  (5*3600+30*60);
+        break;
+    case 35:
+        seconds +=  (5*3600+45*60);
+        break;
+    case 36:
+        seconds +=  6*3600;
+        break;
+    case 37:
+        seconds +=  (6*3600+30*60);
+        break;
+    case 38:
+        seconds +=  7*3600;
+        break;
+    case 39:
+        seconds +=  8*3600;
+        break;
+    case 40:
+        seconds +=  9*3600;
+        break;
+    case 41:
+        seconds +=  (9*3600+30*60);
+        break;
+    case 42:
+        seconds +=  10*3600;
+        break;
+    case 43:
+        seconds +=  (10*3600+30*60);
+        break;
+    case 44:
+        seconds +=  11*3600;
+        break;
+    case 45:
+        seconds +=  (11*3600+30*60);
+        break;
+    case 46:
+        seconds +=  12*3600;
+        break;
+    case 47:
+        seconds +=  (12*3600+45*60);
+        break;
+    case 48:
+        seconds +=  13*3600;
+        break;
+    case 49:
+        seconds +=  14*3600;
+        break;
+
+    }
+
+    //calculation for date
+    calcdatetime(seconds);
+}
+
+void SNTPClient::calcdatetime(tstamp seconds)
+{
+    uint8_t yf=0;
+    tstamp n=0,d=0,total_d=0,rz=0;
+    uint16_t y=0,r=0,yr=0;
+    signed long long yd=0;
+
+    n = seconds;
+    total_d = seconds/(SECS_PERDAY);
+    d=0;
+    uint32_t p_year_total_sec=SECS_PERDAY*365;
+    uint32_t r_year_total_sec=SECS_PERDAY*366;
+    while(n>=p_year_total_sec)
+    {
+        if((EPOCH+r)%400==0 || ((EPOCH+r)%100!=0 && (EPOCH+r)%4==0))
+        {
+            n = n -(r_year_total_sec);
+            d = d + 366;
+        }
+        else
+        {
+            n = n - (p_year_total_sec);
+            d = d + 365;
+        }
+        r+=1;
+        y+=1;
+
+    }
+
+    y += EPOCH;
+
+    Nowdatetime.yy = y;
+
+    yd=0;
+    yd = total_d - d;
+
+    yf=1;
+    while(yd>=28)
+    {
+
+        if(yf==1 || yf==3 || yf==5 || yf==7 || yf==8 || yf==10 || yf==12)
+        {
+            yd -= 31;
+            if(yd<0)break;
+            rz += 31;
+        }
+
+        if (yf==2)
+        {
+            if (y%400==0 || (y%100!=0 && y%4==0))
+            {
+                yd -= 29;
+                if(yd<0)break;
+                rz += 29;
+            }
+            else
+            {
+                yd -= 28;
+                if(yd<0)break;
+                rz += 28;
+            }
+        }
+        if(yf==4 || yf==6 || yf==9 || yf==11 )
+        {
+            yd -= 30;
+            if(yd<0)break;
+            rz += 30;
+        }
+        yf += 1;
+
+    }
+    Nowdatetime.mo=yf;
+    yr = total_d-d-rz;
+
+    yr += 1;
+
+    Nowdatetime.dd=yr;
+
+    //calculation for time
+    seconds = seconds%SECS_PERDAY;
+    Nowdatetime.hh = seconds/3600;
+    Nowdatetime.mm = (seconds%3600)/60;
+    Nowdatetime.ss = (seconds%3600)%60;
+
+}
+
+tstamp SNTPClient::changedatetime_to_seconds(void)
+{
+    tstamp seconds=0;
+    uint32_t total_day=0;
+    uint16_t i=0,run_year_cnt=0,l=0;
+
+    l = Nowdatetime.yy;//low
+
+
+    for(i=EPOCH;i<l;i++)
+    {
+        if((i%400==0) || ((i%100!=0) && (i%4==0)))
+        {
+            run_year_cnt += 1;
+        }
+    }
+
+    total_day=(l-EPOCH-run_year_cnt)*365+run_year_cnt*366;
+
+    for(i=1;i<=Nowdatetime.mo;i++)
+    {
+        if(i==5 || i==7 || i==10 || i==12)
+        {
+            total_day += 30;
+        }
+        if (i==3)
+        {
+            if (l%400==0 && l%100!=0 && l%4==0)
+            {
+                total_day += 29;
+            }
+            else
+            {
+                total_day += 28;
+            }
+        }
+        if(i==2 || i==4 || i==6 || i==8 || i==9 || i==11)
+        {
+            total_day += 31;
+        }
+    }
+
+    seconds = (total_day+Nowdatetime.dd-1)*24*3600;
+    seconds += Nowdatetime.ss;//seconds
+    seconds += Nowdatetime.mm*60;//minute
+    seconds += Nowdatetime.hh*3600;//hour
+
+    return seconds;
+}
diff -r ada7d903623d -r 14382459c8b7 SNTPClient/SNTPClient.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SNTPClient/SNTPClient.h	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,175 @@
+/**
+* @author Raphael Kwon
+*
+* @section LICENSE
+*
+* Copyright (c) 2014 WIZnet Co., Ltd.
+*
+* 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.
+*
+* @section DESCRIPTION
+*    Simple Network Time Protocol Client
+*
+*/
+
+#ifndef SNTPCLIENT_H
+#define SNTPCLIENT_H
+
+#include "mbed.h"
+#include "UDPSocket.h"
+
+/*
+ * @brief Define it for Debug & Monitor DNS processing.
+ * @note 
+ */
+#define _SNTP_DEBUG_
+
+#define MAX_SNTP_BUF_SIZE   sizeof(ntpformat)       ///< maximum size of DNS buffer. */
+#define ntp_port        123                     //ntp server port number
+#define SECS_PERDAY     86400UL                 // seconds in a day = 60*60*24
+#define UTC_ADJ_HRS     9                       // SEOUL : GMT+9
+#define EPOCH           1900                    // NTP start year
+
+/* for ntpclient */
+typedef signed char s_char;
+typedef unsigned long long tstamp;
+typedef unsigned int tdist;
+
+typedef struct _ntpformat
+{
+    uint8_t  dstaddr[4];        /* destination (local) address */
+    char    version;        /* version number */
+    char    leap;           /* leap indicator */
+    char    mode;           /* mode */
+    char    stratum;        /* stratum */
+    char    poll;           /* poll interval */
+    s_char  precision;      /* precision */
+    tdist   rootdelay;      /* root delay */
+    tdist   rootdisp;       /* root dispersion */
+    char    refid;          /* reference ID */
+    tstamp  reftime;        /* reference time */
+    tstamp  org;            /* origin timestamp */
+    tstamp  rec;            /* receive timestamp */
+    tstamp  xmt;            /* transmit timestamp */
+} ntpformat;
+
+typedef struct _datetime
+{
+    uint16_t yy;
+    uint8_t mo;
+    uint8_t dd;
+    uint8_t hh;
+    uint8_t mm;
+    uint8_t ss;
+} datetime;
+
+/** SNTPClient client Class.
+ *
+ * Example (ethernet network):
+ * @code
+ * #include "mbed.h"
+ * #include "EthernetInterface.h"
+ * #include "SNTPClient.h"
+ *
+ * int main() {
+ *    EthernetInterface eth;
+ *    eth.init(); //Use DHCP
+ *    eth.connect();
+ *    printf("IP Address is %s\n\r", eth.getIPAddress());
+ *   
+ *    SNTPClient ws("ws://sockets.mbed.org:443/ws/demo/rw");
+ *    ws.connect();
+ *   
+ *    while (1) {
+ *        int res = ws.send("SNTPClient Hello World!");
+ *
+ *        if (ws.read(recv)) {
+ *            printf("rcv: %s\r\n", recv);
+ *        }
+ *
+ *        wait(0.1);
+ *    }
+ * }
+ * @endcode
+ */
+
+
+class SNTPClient
+{
+    public:
+        /**
+        * Constructor
+        *
+        * @param url The SNTPClient host
+        */
+        SNTPClient(NetworkStack *ns, const char* url, uint8_t time_zone);
+        
+        virtual ~SNTPClient() {close();}
+
+        /**
+        * Connect to the SNTPClient url
+        *
+        *@return true if the connection is established, false otherwise
+        */
+        bool connect();
+
+        /**
+        * Read a SNTPClient message
+        *
+        * @param message pointer to the string to be read (null if drop frame)
+        *
+        * @return true if a SNTPClient frame has been read
+        */
+        bool getTime(datetime *time);
+
+        /**
+        * Close the SNTPClient connection
+        *
+        * @return true if the connection has been closed, false otherwise
+        */
+        bool close();
+
+        /*
+        * Accessor: get host from the SNTPClient url
+        *
+        * @return host
+        */
+        char* getHost();
+
+    private:
+
+        uint16_t port;
+        char host[32];
+
+        UDPSocket *m_udp;
+        NetworkStack *m_ns;
+
+        SocketAddress sntp_server;
+
+        ntpformat NTPformat;
+        datetime Nowdatetime;
+        uint8_t ntpmessage[48];
+        uint8_t tz; // Time Zone
+
+        void get_seconds_from_ntp_server(uint8_t *buf, uint16_t idx);
+        tstamp changedatetime_to_seconds(void);
+        void calcdatetime(tstamp seconds);
+};
+
+#endif
diff -r ada7d903623d -r 14382459c8b7 WebSocketClient.lib
--- a/WebSocketClient.lib	Thu Jun 15 20:25:40 2017 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-https://developer.mbed.org/teams/AMETEK-Powervar/code/WebSocketClient/#860d3b8e21f9
diff -r ada7d903623d -r 14382459c8b7 WebSocketClient/Websocket.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebSocketClient/Websocket.cpp	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,378 @@
+#include "Websocket.h"
+#include "DNSClient.h"
+
+#define MAX_TRY_WRITE 20
+#define MAX_TRY_READ 10
+
+//Debug is disabled by default
+#if 1
+#define DBG(x, ...) std::printf("[WebSocket : DBG]"x"\r\n", ##__VA_ARGS__); 
+#define WARN(x, ...) std::printf("[WebSocket : WARN]"x"\r\n", ##__VA_ARGS__); 
+#define ERR(x, ...) std::printf("[WebSocket : ERR]"x"\r\n", ##__VA_ARGS__); 
+#else
+#define DBG(x, ...) 
+#define WARN(x, ...)
+#define ERR(x, ...) 
+#endif
+
+#define INFO(x, ...) printf("[WebSocket : INFO]"x"\r\n", ##__VA_ARGS__); 
+
+Websocket::Websocket(NetworkStack *ns, char * url) {
+    m_ns = ns;
+    fillFields(url);
+    socket.open(ns);
+    socket.set_blocking(false);
+    socket.set_timeout(400);
+}
+
+void Websocket::fillFields(char * url) {
+  int ret = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
+  if(ret)
+  {
+    ERR("URL parsing failed; please use: \"ws://ip-or-domain[:port]/path\"");
+    return;
+  }
+
+  if(port == 0) //TODO do handle WSS->443
+  {
+    port = 80;
+  }
+  
+  if(strcmp(scheme, "ws"))
+  {
+    ERR("Wrong scheme, please use \"ws\" instead");
+  }
+}
+
+int Websocket::parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen) //Parse URL
+{
+  char* schemePtr = (char*) url;
+  char* hostPtr = (char*) strstr(url, "://");
+  if(hostPtr == NULL)
+  {
+    WARN("Could not find host");
+    return -1; //URL is invalid
+  }
+
+  if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
+  {
+    WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
+    return -1;
+  }
+  memcpy(scheme, schemePtr, hostPtr - schemePtr);
+  scheme[hostPtr - schemePtr] = '\0';
+
+  hostPtr+=3;
+
+  size_t hostLen = 0;
+
+  char* portPtr = strchr(hostPtr, ':');
+  if( portPtr != NULL )
+  {
+    hostLen = portPtr - hostPtr;
+    portPtr++;
+    if( sscanf(portPtr, "%hu", port) != 1)
+    {
+      WARN("Could not find port");
+      return -1;
+    }
+  }
+  else
+  {
+    *port=0;
+  }
+  char* pathPtr = strchr(hostPtr, '/');
+  if( hostLen == 0 )
+  {
+    hostLen = pathPtr - hostPtr;
+  }
+
+  if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
+  {
+    WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
+    return -1;
+  }
+  memcpy(host, hostPtr, hostLen);
+  host[hostLen] = '\0';
+
+  size_t pathLen;
+  char* fragmentPtr = strchr(hostPtr, '#');
+  if(fragmentPtr != NULL)
+  {
+    pathLen = fragmentPtr - pathPtr;
+  }
+  else
+  {
+    pathLen = strlen(pathPtr);
+  }
+
+  if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
+  {
+    WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
+    return -1;
+  }
+  memcpy(path, pathPtr, pathLen);
+  path[pathLen] = '\0';
+
+  return 0;
+}
+
+
+bool Websocket::connect() {
+    DNSClient dns(m_ns);
+    char cmd[200];
+    
+    if (!dns.lookup(host)) {
+        ERR("Websocket: Unable to resolve hostname (%s)", host);
+        wait(0.2);
+        connected = false;
+        return false;
+    }
+    
+    while (socket.connect(dns.get_ip_address(), port) < 0) {
+        ERR("Websocket: Unable to connect to (%s) on port (%d)", host, port);
+        wait(0.2);
+        connected = false;
+        return false;
+    }
+    
+    connected = true;
+
+    // sent http header to upgrade to the ws protocol
+    sprintf(cmd, "GET %s HTTP/1.1\r\n", path);
+    write(cmd, strlen(cmd));
+    
+    sprintf(cmd, "Host: %s:%d\r\n", host, port);
+    write(cmd, strlen(cmd));
+
+    sprintf(cmd, "Upgrade: WebSocket\r\n");
+    write(cmd, strlen(cmd));
+
+    sprintf(cmd, "Connection: Upgrade\r\n");
+    write(cmd, strlen(cmd));
+
+    sprintf(cmd, "Sec-WebSocket-Key: L159VM0TWUzyDxwJEIEzjw==\r\n");
+    write(cmd, strlen(cmd));
+
+    sprintf(cmd, "Sec-WebSocket-Version: 13\r\n\r\n");
+    int ret = write(cmd, strlen(cmd));
+    if (ret != strlen(cmd)) {
+        close();
+        ERR("Could not send request");
+        return false;
+    }
+
+    ret = read(cmd, 200, 100);
+    if (ret < 0) {
+        close();
+        ERR("Could not receive answer\r\n");
+        return false;
+    }
+
+    cmd[ret] = '\0';
+    DBG("recv: %s\r\n", cmd);
+
+    if ( strstr(cmd, "DdLWT/1JcX+nQFHebYP+rqEx5xI=") == NULL ) {
+        ERR("Wrong answer from server, got \"%s\" instead\r\n", cmd);
+        do {
+            ret = read(cmd, 200, 100);
+            if (ret < 0) {
+                ERR("Could not receive answer\r\n");
+                return false;
+            }
+            cmd[ret] = '\0';
+            printf("%s",cmd);
+        } while (ret > 0);
+        close();
+        return false;
+    }
+
+    INFO("\r\nhost: %s\r\npath: %s\r\nport: %d\r\n\r\n", host, path, port);
+    return true;
+}
+
+int Websocket::sendLength(uint32_t len, char * msg) {
+
+    if (len < 126) {
+        msg[0] = len | (1<<7);
+        return 1;
+    } else if (len < 65535) {
+        msg[0] = 126 | (1<<7);
+        msg[1] = (len >> 8) & 0xff;
+        msg[2] = len & 0xff;
+        return 3;
+    } else {
+        msg[0] = 127 | (1<<7);
+        for (int i = 0; i < 8; i++) {
+            msg[i+1] = (len >> i*8) & 0xff;
+        }
+        return 9;
+    }
+}
+
+int Websocket::readChar(char * pC, bool block) {
+    return read(pC, 1, 1);
+}
+
+int Websocket::sendOpcode(uint8_t opcode, char * msg) {
+    msg[0] = 0x80 | (opcode & 0x0f);
+    return 1;
+}
+
+int Websocket::sendMask(char * msg) {
+    for (int i = 0; i < 4; i++) {
+        msg[i] = 0;
+    }
+    return 4;
+}
+
+int Websocket::send(char * str) {
+    char msg[strlen(str) + 15];
+    int idx = 0;
+    idx = sendOpcode(0x01, msg);
+    idx += sendLength(strlen(str), msg + idx);
+    idx += sendMask(msg + idx);
+    memcpy(msg+idx, str, strlen(str));
+    int res = write(msg, idx + strlen(str));
+    return res;
+}
+
+
+bool Websocket::read(char * message) {
+    int i = 0;
+    uint32_t len_msg;
+    char opcode = 0;
+    char c;
+    char mask[4] = {0, 0, 0, 0};
+    bool is_masked = false;
+    Timer tmr;
+
+    // read the opcode
+    tmr.start();
+    while (true) {
+        if (tmr.read() > 3) {
+            DBG("timeout ws\r\n");
+            return false;
+        }
+        
+        if(!connected)
+        {
+            WARN("Connection was closed by server");
+            return false;
+        }
+
+        socket.set_blocking(false);
+        socket.set_timeout(1);
+        if (socket.recv(&opcode, 1) != 1) {
+            socket.set_blocking(false);
+            socket.set_timeout(2000);
+            return false;
+        }
+
+        socket.set_blocking(false);
+        socket.set_timeout(2000);
+
+        if (opcode == 0x81)
+            break;
+    }
+    DBG("opcode: 0x%X\r\n", opcode);
+
+    readChar(&c);
+    len_msg = c & 0x7f;
+    is_masked = c & 0x80;
+    if (len_msg == 126) {
+        readChar(&c);
+        len_msg = c << 8;
+        readChar(&c);
+        len_msg += c;
+    } else if (len_msg == 127) {
+        len_msg = 0;
+        for (int i = 0; i < 8; i++) {
+            readChar(&c);
+            len_msg += (c << (7-i)*8);
+        }
+    }
+
+    if (len_msg == 0) {
+        return false;
+    }
+    DBG("length: %d\r\n", len_msg);
+    
+    if (is_masked) {
+        for (i = 0; i < 4; i++)
+            readChar(&c);
+        mask[i] = c;
+    }
+
+    int nb = read(message, len_msg, len_msg);
+    if (nb != len_msg)
+        return false;
+
+    for (i = 0; i < len_msg; i++) {
+        message[i] = message[i] ^ mask[i % 4];
+    }
+
+    message[len_msg] = '\0';
+
+    return true;
+}
+
+bool Websocket::close() {
+    if (!is_connected())
+        return false;
+
+    int ret = socket.close();
+    if (ret < 0) {
+        ERR("Could not disconnect");
+        return false;
+    }
+    return true;
+}
+
+bool Websocket::is_connected() {
+    return connected;
+}
+
+char* Websocket::getPath() {
+    return path;
+}
+
+int Websocket::write(char * str, int len) {
+    int res = 0, idx = 0;
+    
+    for (int j = 0; j < MAX_TRY_WRITE; j++) {
+    
+        if(!connected)
+        {
+            WARN("Connection was closed by server");
+            break;
+        }
+
+        if ((res = socket.send(str + idx, len - idx)) == -1)
+            continue;
+
+        idx += res;
+        
+        if (idx == len)
+            return len;
+    }
+    
+    return (idx == 0) ? -1 : idx;
+}
+
+int Websocket::read(char * str, int len, int min_len) {
+    int res = 0, idx = 0;
+    
+    for (int j = 0; j < MAX_TRY_WRITE; j++) {
+
+        if ((res = socket.recv(str + idx, len - idx)) == -1)
+            continue;
+
+        idx += res;
+        
+        if (idx == len || (min_len != -1 && idx > min_len))
+            return idx;
+    }
+    
+    return (idx == 0) ? -1 : idx;
+}
diff -r ada7d903623d -r 14382459c8b7 WebSocketClient/Websocket.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebSocketClient/Websocket.h	Thu Jun 15 20:29:03 2017 +0000
@@ -0,0 +1,144 @@
+/**
+* @author Samuel Mokrani
+*
+* @section LICENSE
+*
+* Copyright (c) 2011 mbed
+*
+* 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.
+*
+* @section DESCRIPTION
+*    Simple websocket client
+*
+*/
+
+#ifndef WEBSOCKET_H
+#define WEBSOCKET_H
+
+#include "mbed.h"
+
+/** Websocket client Class.
+ *
+ * Example (ethernet network):
+ * @code
+ * #include "mbed.h"
+ * #include "EthernetInterface.h"
+ * #include "Websocket.h"
+ *
+ * int main() {
+ *    EthernetInterface eth;
+ *    eth.init(); //Use DHCP
+ *    eth.connect();
+ *    printf("IP Address is %s\n\r", eth.getIPAddress());
+ *   
+ *    Websocket ws("ws://sockets.mbed.org:443/ws/demo/rw");
+ *    ws.connect();
+ *   
+ *    while (1) {
+ *        int res = ws.send("WebSocket Hello World!");
+ *
+ *        if (ws.read(recv)) {
+ *            printf("rcv: %s\r\n", recv);
+ *        }
+ *
+ *        wait(0.1);
+ *    }
+ * }
+ * @endcode
+ */
+ 
+class Websocket
+{
+    public:
+        /**
+        * Constructor
+        *
+        * @param url The Websocket url in the form "ws://ip_domain[:port]/path" (by default: port = 80)
+        */
+        Websocket(NetworkStack *ns, char * url);
+
+        /**
+        * Connect to the websocket url
+        *
+        *@return true if the connection is established, false otherwise
+        */
+        bool connect();
+
+        /**
+        * Send a string according to the websocket format (see rfc 6455)
+        *
+        * @param str string to be sent
+        *
+        * @returns the number of bytes sent
+        */
+        int send(char * str);
+
+        /**
+        * Read a websocket message
+        *
+        * @param message pointer to the string to be read (null if drop frame)
+        *
+        * @return true if a websocket frame has been read
+        */
+        bool read(char * message);
+
+        /**
+        * To see if there is a websocket connection active
+        *
+        * @return true if there is a connection active
+        */
+        bool is_connected();
+
+        /**
+        * Close the websocket connection
+        *
+        * @return true if the connection has been closed, false otherwise
+        */
+        bool close();
+
+        /*
+        * Accessor: get path from the websocket url
+        *
+        * @return path
+        */
+        char* getPath();
+
+    private:
+
+        void fillFields(char * url);
+        int parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen); //Parse URL
+        int sendOpcode(uint8_t opcode, char * msg);
+        int sendLength(uint32_t len, char * msg);
+        int sendMask(char * msg);
+        int readChar(char * pC, bool block = true);
+        
+        char scheme[8];
+        uint16_t port;
+        char host[32];
+        char path[64];
+        bool connected;
+        
+        TCPSocket socket;
+        NetworkStack *m_ns;
+
+        int read(char * buf, int len, int min_len = -1);
+        int write(char * buf, int len);
+};
+
+#endif