esp8266 websocket and socket

Revision:
0:46166e7d81a8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ESP8266.cpp	Fri Jun 12 10:06:12 2015 +0000
@@ -0,0 +1,481 @@
+#include "mbed.h"
+#include "ESP8266.h"
+
+#define MAX_TRY_WRITE 20
+#define MAX_TRY_READ 10
+
+//Debug is disabled by default
+#if 0
+#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__); 
+
+esp8266* esp8266::inst;
+
+esp8266::esp8266(PinName tx,PinName rx,int baudRate, const char *ssid, const char *password,const char *host,const char *port) : Modem(tx,rx,baudRate)
+{
+    inst = this;
+    _ssid=ssid;
+    _password=password;
+    _host=host;
+    _port=port;
+}
+
+bool esp8266::join()
+{
+    char cmd[64];
+    int r;
+    //disable echo
+    sendCmd("ATE0\r\n");
+
+    r=sendCmdAndWaitForResp("AT+CWMODE=1\r\n","OK",DEFAULT_TIMEOUT,CMD);
+    printf("wifi.CWMODE=%d\r\n",r);
+
+    snprintf(cmd,sizeof(cmd),"AT+CWJAP=\"%s\",\"%s\"\r\n",_ssid,_password);
+    printf("mensaje=%s\r\n",cmd);
+    r=sendCmdAndWaitForResp(cmd,"OK",15,CMD);
+    printf("wifi.CWJAP=%d\r\n",r);
+
+    if(r==0) {
+        getip();
+        return true;
+    } else {
+        return false;
+    }
+
+
+}
+
+int esp8266::connect(int socket)
+{
+    char cmd[64];
+    int r;
+
+    r=sendCmdAndWaitForResp("AT+CIPMUX=1\r\n","OK",DEFAULT_TIMEOUT,CMD);
+    printf("socket.CIPMUX1=%d\r\n",r);
+
+
+    sprintf(cmd,"AT+CIPSTART=%d,\"TCP\",\"%s\",%s\r\n",socket,_host,_port);
+    printf("mensaje2=%s\r\n",cmd);
+    r=sendCmdAndWaitForResp(cmd,"CONNECT",15,CMD);
+    printf("socket.AT+CIPSTART=%d\r\n",r);
+
+    return r;
+}
+
+int esp8266::send(int socket,const char * str,int len)
+{
+
+    char cmd[100];
+    cleanBuffer(cmd,100);
+    wait(0.3);
+    if(len > 0) {
+        snprintf(cmd,sizeof(cmd),"AT+CIPSEND=%d,%d\r\n",socket,len);
+        printf("mensaje3=%s\r\n",cmd);
+        if(0 != sendCmdAndWaitForResp(cmd,">",2,CMD)) {
+            return -1;
+        }
+        sendCmd(str);
+
+    }
+    return len;
+}
+
+bool esp8266::disconnect(int socket)
+{
+    char cmd[35];
+    sprintf(cmd,"AT+CIPCLOSE=%d\r\n",socket);
+    sendCmd(cmd);
+
+    return true;
+}
+
+
+void esp8266::reset()
+{
+    sendCmdAndWaitForResp("AT+CWMODE=1\r\n","OK",DEFAULT_TIMEOUT,CMD);
+    sendCmd("AT+RST\r\n");
+}
+
+bool esp8266::is_connected()   //RETURN  2 GOT IP  3 CONNECTED   4 DISCONNECTED
+{
+    char resp[20];
+
+    sendCmd("AT+CIPSTATUS\r\n");
+    readBuffer(resp,sizeof(resp),DEFAULT_TIMEOUT/2);
+    if(NULL != strstr(resp,"STATUS:3")) {
+        //+CIPSTATUS: 1,0,"TCP","216.52.233.120","80","CONNECTED"
+        return true;
+    } else {
+        //+CIPSTATUS: 1,0,"TCP","216.52.233.120","80","CLOSED"
+        //+CIPSTATUS: 0,,"","","","INITIAL"
+        return false;
+    }
+}
+
+void esp8266::getip()
+{
+    sendCmd("AT+CIFSR\r\n");
+    readBuffer(_ip,100,10);
+}
+
+void esp8266::set_sta_ip(char * s_ip)
+{
+    int r;
+    char cmd[30];
+    sprintf(cmd,"AT+CIPSTA=\"%s\"\r\n",s_ip);
+    r=sendCmdAndWaitForResp(cmd,"OK",5,CMD);
+    printf("mensaje4=%s\r\n",cmd);
+    printf("cambio ip=%d\r\n",r);
+    getip();
+}
+
+void esp8266::list_ap()
+{
+    int r;
+    r=sendCmdAndWaitForResp("AT+CWLAP\r\n","+",5,CMD);
+    printf("cambio ip=%d\r\n",r);
+    readBuffer(_lap,2000,10);
+}
+
+int esp8266::recv(char* buf, int len)
+{
+    cleanBuffer(buf,len);
+    readBuffer(buf,len,DEFAULT_TIMEOUT/2);
+    return len;
+    //return strlen(buf);
+}
+
+//----------------------------------websocket-------------------------------------------------------
+
+void esp8266::websocket(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 esp8266::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 esp8266::w_connect(int socket) {
+    char cmd[200];
+/*
+    while (connect(socket) < 0) {
+        ERR("Unable to connect to (%s) on port (%d)", host, port);
+        wait(0.2);
+        return false;
+    }
+*/
+    // sent http header to upgrade to the ws protocol
+    
+    printf("path=%s\r\n",path);
+    printf("host=%s\r\n",host);
+    printf("port=%d\r\n",port);
+    
+    sprintf(cmd, "GET %s HTTP/1.1\r\n", path);
+    printf("n-GET=%d\r\n",strlen(cmd));
+    printf("%s\r\n",cmd);
+    write(cmd, strlen(cmd));
+    
+    sprintf(cmd, "Host: %s:%d\r\n", host, port);
+    printf("n-host=%d\r\n",strlen(cmd));
+    printf("%s\r\n",cmd);
+    write(cmd, strlen(cmd));
+
+    sprintf(cmd, "Upgrade: WebSocket\r\n");
+    printf("n-up=%d\r\n",strlen(cmd));
+    printf("%s\r\n",cmd);
+    write(cmd, strlen(cmd));
+
+    sprintf(cmd, "Connection: Upgrade\r\n");
+    printf("n-con=%d\r\n",strlen(cmd));
+    printf("%s\r\n",cmd);
+    write(cmd, strlen(cmd));
+
+    sprintf(cmd, "Sec-WebSocket-Key: L159VM0TWUzyDxwJEIEzjw==\r\n");
+    printf("n-key=%d\r\n",strlen(cmd));
+    printf("%s\r\n",cmd);
+    write(cmd, strlen(cmd));
+
+    sprintf(cmd, "Sec-WebSocket-Version: 13\r\n\r\n");
+    printf("n-ver=%d\r\n",strlen(cmd));
+    printf("%s\r\n",cmd);
+    int ret = write(cmd, strlen(cmd));
+    if (ret != strlen(cmd)) {
+        disconnect(socket);
+        ERR("Could not send request");
+        return false;
+    }
+
+    ret = read(cmd, 200, 100);
+    if (ret < 0) {
+        disconnect(socket);
+        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);
+        disconnect(socket);
+        return false;
+    }
+
+    INFO("\r\nhost: %s\r\npath: %s\r\nport: %d\r\n\r\n", host, path, port);
+    return true;
+}
+
+int esp8266::sendLength(uint32_t len, char * msg) { //sendlength(5,msg+1)
+
+    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 esp8266::readChar(char * pC, bool block) {
+    return read(pC, 1, 1);
+}
+
+int esp8266::sendOpcode(uint8_t opcode, char * msg) {
+    msg[0] = 0x80 | (opcode & 0x0f);
+    return 1;
+}
+
+int esp8266::sendMask(char * msg) {
+    for (int i = 0; i < 4; i++) {
+        msg[i] = 0;
+    }
+    return 4;
+}
+
+int esp8266::w_send(char * str) {
+    char msg[strlen(str) + 15];   //msg[20]
+    int idx = 0;
+    idx = sendOpcode(0x01, msg);  //msg[0]=0x81
+    idx += sendLength(strlen(str), msg + idx);// sendlength(5,msg+1)  idx=2
+    idx += sendMask(msg + idx);  //idx=6
+    memcpy(msg+idx, str, strlen(str));
+    int res = write(msg, idx + strlen(str));
+    return res;
+}
+
+bool esp8266::w_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(!is_connected())
+        {
+            WARN("Connection was closed by server");
+            return false;
+        }
+
+        //----------socket.set_blocking(false, 1);
+        if (recv(&opcode, 1) != 1) {
+            
+            //socket.set_blocking(false, 2000);
+            return false;
+        }
+
+        //-----------socket.set_blocking(false, 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;
+}
+char* esp8266::getPath() {
+    return path;
+}
+
+int esp8266::write(char * str, int len) {
+    int res = 0, idx = 0;
+    
+    for (int j = 0; j < MAX_TRY_WRITE; j++) {
+    /*
+        if(!is_connected())
+        {
+            WARN("Connection was closed by server");
+            break;
+        }
+    */
+        if ((res = send(4,str + idx,len - idx)) == -1)
+            continue;
+
+        idx += res;
+        
+        if (idx == len)
+            return len;
+    }
+    
+    return (idx == 0) ? -1 : idx;
+}
+
+int esp8266::read(char * str, int len, int min_len) {
+    int res = 0, idx = 0;
+    
+    for (int j = 0; j < MAX_TRY_WRITE; j++) {
+
+        if ((res = 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;
+}
+
+