esp8266 websocket and socket

ESP8266.cpp

Committer:
metabi814
Date:
2015-06-12
Revision:
0:46166e7d81a8

File content as of revision 0:46166e7d81a8:

#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;
}