A library for talking to Multi-Tech's Cellular SocketModem Devices.

Dependents:   M2X_dev axeda_wrapper_dev MTS_M2x_Example1 MTS_Cellular_Connect_Example ... more

wifi/Wifi.cpp

Committer:
jengbrecht
Date:
2013-12-30
Revision:
98:dbeac735109d
Parent:
95:4fdf968b5b37
Child:
99:eba6b99bc80c

File content as of revision 98:dbeac735109d:

#include "Wifi.h"
#include <string>

Wifi* Wifi::instance = NULL;

Wifi* Wifi::getInstance()
{
    if(instance == NULL) {
        instance = new Wifi(NULL);
    }
    return instance;
}

bool Wifi::init(MTSBufferedIO* io)
{
    if (io == NULL) {
        return false;
    }
    instance->io = io;
    //Set device into command mode
    if (!setCmdMode(true)) {
        return false;
    }

    //Set device to non-echo mode
    if (sendBasicCommand("set uart mode 1", 1000) != SUCCESS) {
        printf("[ERROR] Failed to set to non-echo mode\n\r");
        return false;
    }

    //Set device to manual infrastructure mode
    if (sendBasicCommand("set wlan join 0", 1000) != SUCCESS) {
        printf("[ERROR] Failed to set join mode\n\r");
        return false;
    }

    //Set device to channel auto-scanning mode
    if (sendBasicCommand("set wlan channel 0", 1000) != SUCCESS) {
        printf("[ERROR] Failed to set auto-scanning mode\n\r");
        return false;
    }

    //Set device so no data is transmitted immediately following a socket connection
    if (sendBasicCommand("set comm remote 0", 1000) != SUCCESS) {
        printf("[ERROR] Failed to set remote transmit mode\n\r");
        return false;
    }
    return true;
}

Wifi::Wifi(MTSBufferedIO* io)
    : io(io)
    , wifiConnected(false)
    , _ssid("")
    , mode(TCP)
    , socketOpened(false)
    , socketCloseable(true)
    , local_port(0)
    , host_port(0)
    , cmdOn(false)
{

}

Wifi::~Wifi()
{
}

bool Wifi::connect()
{
    //Check if socket is open
    if(socketOpened) {
        return true;
    }

    //Run Test first to validate a good state
    if(isConnected()) {
        return true;
    }

    if (_ssid.size() == 0) {
        printf("[ERROR] No SSID has been set\n\r");
        return false;
    }

    if(!setCmdMode(true)) {
        return false;
    }

    //Check RSSI: AT+CSQ
    int rssi = getSignalStrength();
    printf("[DEBUG] Signal strength (dBm): %d\r\n", rssi);

    //Possibly add a scan command here and look for the network....

    //Set device into DHCP mode
    if (sendBasicCommand("set ip dhcp 1", 1000) != SUCCESS) {
        return false;
    }

    //join my_network
    printf("[DEBUG] Making SSID Connection Attempt. SSID[%s]\r\n", _ssid.c_str());
    std::string result = sendCommand("join " + _ssid, 15000, "Listen");
    //printf("Connect Status: %s\n\r", result.c_str());

    //Check whether connection was successful
    if(result.find("Associated!") != string::npos) {
        int start = result.find("IP=");
        int stop = result.find(":", start);
        local_address = result.substr(start + 3, stop - start - 3);
        printf("[INFO] WiFi Connection Established: IP[%s]\r\n", local_address.c_str());
        wifiConnected = true;
    } else {
        wifiConnected = false;
    }

    return wifiConnected;
}

void Wifi::disconnect()
{
    printf("[DEBUG] Disconnecting from network\r\n");

    if(socketOpened) {
        close();
    }

    if(!setCmdMode(true)) {
        printf("[ERROR] Failed in disconnecting from network.  Continuing ...\r\n");
    }

    std::string response = sendCommand("leave", 20000);
    printf("Response: %s\n\r", response.c_str());
    if (response.find("DeAuth") != string::npos) {
        printf("[DEBUG] Successfully disconnected from network\r\n");
    } else {
        printf("[ERROR] Failed in disconnecting from network.  Continuing ...\r\n");
    }

    wifiConnected = false;
}

bool Wifi::isConnected()
{
    //1) Check if SSID was set
    if(_ssid.size() == 0) {
        printf("[DEBUG] SSID is not set\r\n");
        return false;
    }

    //1) Check that we do not have a live connection up
    if(socketOpened) {
        printf("[DEBUG] Socket is opened\r\n");
        return true;
    }

    //Check command mode.
    if(!setCmdMode(true)) {
        return false;
    }

    //2) Query the wifi module
    wifiConnected = false;
    std::string result = sendCommand("show net", 5000, "Links");
    //printf("netResult: %s\n\r", result);
    if(result.find("Assoc=OK") != std::string::npos) {
        wifiConnected = true;
    }

    return wifiConnected;
}

bool Wifi::bind(unsigned int port)
{
    if(socketOpened) {
        printf("[ERROR] socket is open. Can not set local port\r\n");
        return false;
    }
    if(port > 65535) {
        printf("[ERROR] port out of range (0-65535)\r\n");
        return false;
    }
    local_port = port;
    return true;
}

bool Wifi::open(const std::string& address, unsigned int port, Mode mode)
{
    //set comm size??? are advanced Socket settings
    //set comm time??? are advanced Socket settings
    char buffer[256] = {0};
    printf("[DEBUG] Attempting to Open Socket\r\n");

    //1) Check that we do not have a live connection up
    if(socketOpened) {
        //Check that the address, port, and mode match
        if(host_address != address || host_port != port || this->mode != mode) {
            if(this->mode == TCP) {
                printf("[ERROR] TCP socket already opened (%s:%d)\r\n", host_address.c_str(), host_port);
            } else {
                printf("[ERROR] UDP socket already opened (%s:%d)\r\n", host_address.c_str(), host_port);
            }
            return false;
        }

        printf("[DEBUG] Socket already opened\r\n");
        return true;
    }

    //2) Check Parameters
    if(port > 65535) {
        printf("[ERROR] port out of range (0-65535)\r\n");
        return false;
    }


    //3) Check Wifi network connection
    if(!isConnected()) {
        printf("[ERROR] Wifi network not connected.  Attempting to connect\r\n");
        if(!connect()) {
            printf("[ERROR] Wifi network connection failed\r\n");
            return false;
        } else {
            printf("[DEBUG] Wifi connection established\r\n");
        }
    }

    //Check command mode
    if(!setCmdMode(true)) {
        return false;
    }

    //Set Local Port
    if(local_port != 0) {
        //Attempt to set local port
        sprintf(buffer, "set ip localport %d", local_port);
        Code code = sendBasicCommand(buffer, 1000);
        if(code != SUCCESS) {
            printf("[WARNING] Unable to set local port (%d) [%d]. Continuing...\r\n", local_port, (int) code);
        }
    }

    //Set TCP/UDP parameters
    sprintf(buffer, "set ip remote %d", port);
    if(sendBasicCommand(buffer, 1000) == SUCCESS) {
        host_port = port;
    } else {
        printf("[ERROR] Host port could not be set\r\n");
    }

    if(sendBasicCommand("set ip host " + address, 1000) == SUCCESS) {
        host_address = address;
    } else {
        printf("[ERROR] Host address could not be set\r\n");
    }

    if(sendBasicCommand("set ip host " + address, 1000) == SUCCESS) {
        host_address = address;
    } else {
        printf("[ERROR] Host address could not be set\r\n");
    }

    // Try and Connect
    std::string sMode;
    std::string sOpenSocketCmd;
    if(mode == TCP) {
        sOpenSocketCmd = "open";
        sMode = "TCP";
    } else {
        sOpenSocketCmd = "AT#OUDP";
        sMode = "UDP";
    }
    string response = sendCommand(sOpenSocketCmd, 20000, "OPEN");
    printf("Open Response: %s\n\r", response.c_str());
    if (response.find("OPEN") != string::npos) {
        printf("[INFO] Opened %s Socket [%s:%d]\r\n", sMode.c_str(), address.c_str(), port);
        socketOpened = true;
        cmdOn = false; //Need to make sure that this is true...
    } else {
        printf("[WARNING] Unable to open %s Socket [%s:%d]\r\n", sMode.c_str(),  address.c_str(), port);
        socketOpened = false;
    }

    return socketOpened;
}

bool Wifi::isOpen()
{
    return socketOpened;
}

bool Wifi::close()
{
    if(io == NULL) {
        printf("[ERROR] MTSBufferedIO not set\r\n");
        return false;
    }

    if(!socketOpened) {
        printf("[WARNING] Socket close() called, but socket was not open\r\n");
        return true;
    }

    if(!setCmdMode(true)) {
        printf("[ERROR] Failed to close socket\r\n");
        return false;
    }

    std::string response = sendCommand("close", 10000, "CLOS");
    if(response.find("CLOS") == string::npos) {
        printf("[ERROR] Failed to successfully close socket\r\n");
        return false;
    }

    socketOpened = false;
    return true;
}

int Wifi::read(char* data, int max, int timeout)
{
    if(io == NULL) {
        printf("[ERROR] MTSBufferedIO not set\r\n");
        return -1;
    }

    //Check that nothing is in the rx buffer
    if(!socketOpened && !io->readable()) {
        printf("[ERROR] Socket is not open\r\n");
        return -1;
    }

    //Check for data mode
    if(!setCmdMode(false)) {
        printf("[ERROR] Failed to read data due to mode\r\n");
        return -1;
    }

    int bytesRead = 0;

    if(timeout >= 0) {
        bytesRead = io->read(data, max, static_cast<unsigned int>(timeout));
    } else {
        bytesRead = io->read(data, max);
    }

    if(bytesRead > 0 && socketCloseable) {
        //Remove escape characters
        int index = 0;
        bool escapeFlag = false;
        for(int i = 0; i < bytesRead; i++) {
            if(data[i] == DLE || data[i] == ETX) {
                if(escapeFlag == true) {
                    //This character has been escaped
                    escapeFlag = false;
                } else if(data[bytesRead] == DLE) {
                    //Found escape character
                    escapeFlag = true;
                    continue;
                } else {
                    //ETX sent without escape -> Socket closed
                    printf("[INFO] Read ETX character without DLE escape. Socket closed\r\n");
                    socketOpened = false;
                    continue;
                }
            }

            if(index != i) {
                data[index] = data[i];
            }
            index++;
        }
        bytesRead = index;
    }

    //Scan for socket closed message
    for(size_t i = 0; i < bytesRead; i++) {
        if(data[i] == 'O') {
            if(strstr(&data[i], "Ok_Info_SocketClosed")) {
                printf("[INFO] Found socket closed message. Socket closed\r\n");
                //Close socket and Cut Off End of Message
                socketOpened = false;
                data[i] = '\0';
                bytesRead = i;
                break;
            }
        }
    }
    return bytesRead;
}

int Wifi::write(const char* data, int length, int timeout)
{
    if(io == NULL) {
        printf("[ERROR] MTSBufferedIO not set\r\n");
        return -1;
    }

    if(!socketOpened) {
        printf("[ERROR] Socket is not open\r\n");
        return -1;
    }

    //Check for data mode
    if(!setCmdMode(false)) {
        printf("[ERROR] Failed to write data due to mode\r\n");
        return -1;
    }

    int bytesWritten = 0;
    if(timeout >= 0) {
        Timer tmr;
        tmr.start();
        do {
            int available = io->writeable();
            if (available > 0) {
                int size = MIN(available, length - bytesWritten);
                bytesWritten += io->write(&data[bytesWritten], size);
            } else {
                wait(0.05);
            }
        } while (tmr.read_ms() <= timeout && bytesWritten < length);
    } else {
        bytesWritten = io->write(&data[bytesWritten], length - bytesWritten);
    }

    return bytesWritten;
}

unsigned int Wifi::readable()
{
    if(io == NULL) {
        printf("[ERROR] MTSBufferedIO not set\r\n");
        return 0;
    }
    if(!socketOpened) {
        printf("[ERROR] Socket is not open\r\n");
        return 0;
    }
    return io->readable();
}

unsigned int Wifi::writeable()
{
    if(io == NULL) {
        printf("[ERROR] MTSBufferedIO not set\r\n");
        return 0;
    }
    if(!socketOpened) {
        printf("[ERROR] Socket is not open\r\n");
        return 0;
    }

    return io->writeable();
}

void Wifi::reset()
{
}

Code Wifi::setNetwork(const std::string& ssid, const std::string& key, SecurityType type)
{
    //Check the command mode
    if(!setCmdMode(true)) {
        return FAILURE;
    }

    Code code;

    //Set the appropraite SSID
    code = sendBasicCommand("set wlan ssid " + ssid, 1000);
    if (code != SUCCESS) {
        return code;
    }

    //Set the security key
    if (type == WEP64 || type == WEP128) {
        //Set the WEP key if using WEP encryption
        code = sendBasicCommand("set wlan key " + key, 1000);
        if (code != SUCCESS) {
            return code;
        }
    } else if (type == WPA || type == WPA2) {
        //Set the WPA key if using WPA encryption
        code = sendBasicCommand("set wlan phrase " + key, 1000);
        if (code != SUCCESS) {
            return code;
        }
    }

    _ssid = ssid;
    return SUCCESS;
}

Code Wifi::setDNS(const std::string& dnsName)
{
    //Check the command mode
    if(!setCmdMode(true)) {
        return FAILURE;
    }

    return sendBasicCommand("set dns name " + dnsName, 1000);
}

int Wifi::getSignalStrength()
{
    //Check the command mode
    if(!setCmdMode(true)) {
        printf("[ERROR] Could not get RSSI\n\r");
        return -1;
    }

    string response = sendCommand("show rssi", 2000, "dBm");
    if (response.find("RSSI") == string::npos) {
        printf("[ERROR] Could not get RSSI\n\r");
        return -1;
    }
    int start = response.find('(');
    int stop = response.find(')', start);
    string signal = response.substr(start + 1, stop - start - 1);
    int value;
    sscanf(signal.c_str(), "%d", &value);
    return value;
}

bool Wifi::ping(const std::string& address)
{
    //Check the command mode
    if(!setCmdMode(true)) {
        printf("[ERROR] Could not send ping command\n\r");
        return false;
    }

    std::string response;
    for (int i = 0; i < PINGNUM; i++) {
        response = sendCommand("ping " + address, PINGDELAY * 1000, "reply");
        if (response.find("reply") != std::string::npos) {
            return true;
        }
    }
    return false;
}

bool Wifi::setCmdMode(bool on)
{
    if (on) {
        if (cmdOn) {
            return true;
        }
        wait(.5);
        std::string response = sendCommand("$$", 2000, "CMD", '$');
        if (response.find("CMD") != string::npos) {
            cmdOn = true;
            return true;
        }
        printf("[ERROR] Failed to enter command mode\n\r");
        return false;
    } else {
        if (!cmdOn) {
            return true;
        }
        std::string response = sendCommand("exit", 2000, "EXIT");
        if (response.find("EXIT") != string::npos) {
            cmdOn = false;
            return true;
        }
        printf("[ERROR] Failed to exit command mode\n\r");
        return false;
    }
}

Code Wifi::sendBasicCommand(string command, int timeoutMillis, char esc)
{
    if(socketOpened) {
        printf("[ERROR] socket is open. Can not send AT commands\r\n");
        return ERROR;
    }

    string response = sendCommand(command, timeoutMillis, "AOK", esc);
    //printf("Response: %s\n\r", response.c_str());
    if (response.size() == 0) {
        return NO_RESPONSE;
    } else if (response.find("AOK") != string::npos) {
        return SUCCESS;
    } else if (response.find("ERR") != string::npos) {
        return ERROR;
    } else {
        return FAILURE;
    }
}

string Wifi::sendCommand(string command, int timeoutMillis, std::string response, char esc)
{
    if(io == NULL) {
        printf("[ERROR] MTSBufferedIO not set\r\n");
        return "";
    }
    if(socketOpened && command.compare("$$") != 0 && command.compare("exit") != 0) {
        printf("[ERROR] socket is open. Can not send AT commands\r\n");
        return "";
    }

    io->rxClear();
    io->txClear();
    std::string result;

    //Attempt to write command
    if(io->write(command.data(), command.size(), timeoutMillis) != command.size()) {
        //Failed to write command
        printf("[ERROR] failed to send command to radio within %d milliseconds\r\n", timeoutMillis);
        return "";
    }

    //Send Escape Character
    if (esc != 0x00) {
        if(io->write(esc, timeoutMillis) != 1) {
            printf("[ERROR] failed to send '%c' to radio within %d milliseconds\r\n", esc, timeoutMillis);
            return "";
        }
    }

    int timer = 0;
    size_t previous = 0;
    char tmp[256];
    tmp[255] = 0;
    bool done = false;
    do {
        wait(.2);
        timer = timer + 200;
        previous = result.size();
        int size = io->read(tmp, 255, 0);    //1 less than allocated
        if(size > 0) {
            result.append(tmp, size);
            if (response.size() != 0) {
                if (result.find(response) != string::npos) {
                    return result;
                }
            } else {
                done =  (result.size() == previous);
            }
        }
        if(timer >= timeoutMillis) {
            printf("[WARNING] sendCommand [%s] timed out after %d milliseconds\r\n", command.c_str(), timeoutMillis);
            done = true;
        }
    } while (!done);

    //printf("Result: %s\n\r", result.c_str());
    return result;
}