- Added setBaud() function - Added CheckNetworkStatus() function - Improved messaging system

Dependents:   IoT_Ex BatteryModelTester BatteryModelTester

Fork of WiflyInterface by Components

Wifly/Wifly.cpp

Committer:
defrost
Date:
2016-11-24
Revision:
38:e08d655acee6
Parent:
36:8774837f5a78

File content as of revision 38:e08d655acee6:

/* 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.
 */

#include "mbed.h"
#include "Wifly.h"
#include <string>
#include <algorithm>

//#define DEBUG
#define INFOMESSAGES
#define WARNMESSAGES
#define ERRMESSAGES
#define FUNCNAME "Wifly"
#include "messages.h"

#define MAX_TRY_JOIN 3

Wifly * Wifly::inst;

Wifly::Wifly(   PinName tx, PinName rx, PinName _reset, PinName tcp_status, const char * ssid, const char * phrase, Security sec):
    wifi(tx, rx), reset_pin(_reset), tcp_status(tcp_status), buf_wifly(1024)
{
    // Set the default baud rate:
    serial_baudrate = 9600;
    
    memset(&state, 0, sizeof(state));
    state.sec = sec;

    // change all ' ' in '$' in the ssid and the passphrase
    strcpy(this->ssid, ssid);
    for (int i = 0; i < strlen(ssid); i++) {
        if (this->ssid[i] == ' ')
            this->ssid[i] = '$';
    }
    strcpy(this->phrase, phrase);
    for (int i = 0; i < strlen(phrase); i++) {
        if (this->phrase[i] == ' ')
            this->phrase[i] = '$';
    }

    inst = this;
    attach_rx(false);
    state.cmd_mode = false;
    DBG("Wifly Constructed");
}

void Wifly::setBaud(int baudrate)
{
    char cmd[28];
    // Save the baudrate:
    if(baudrate > 0){
        serial_baudrate = baudrate;
    }
    DBG("Setting wifi shield baudrate to: %d.", serial_baudrate);
    // This sets the baud rate 'instantly'
    sprintf(cmd, "set u i %d\r", serial_baudrate);
    // This one sets it some other way that does not work
    //sprintf(cmd, "set u b %d\r", baudrate);
    // Set baud rate of wifly:
    sendCommand(cmd, NULL);
    wait(0.5);
    // Set baud rate of UART:
    wifi.baud(serial_baudrate);
    wait(0.5);
    exit();
}

bool Wifly::join()
{
    char cmd[28];

    for (int i= 0; i < MAX_TRY_JOIN; i++) {

        // no auto join
        if (!sendCommand("set w j 0\r", "AOK"))
            continue;

        //no echo
        if (!sendCommand("set u m 1\r", "AOK"))
            continue;

        // set time
        if (!sendCommand("set c t 30\r", "AOK"))
            continue;

        // set size
        if (!sendCommand("set c s 1024\r", "AOK"))
            continue;

        // red led on when tcp connection active
        if (!sendCommand("set s i 0x40\r", "AOK"))
            continue;

        // no string sent to the tcp client
        if (!sendCommand("set c r 0\r", "AOK"))
            continue;

        // tcp protocol
        if (!sendCommand("set i p 2\r", "AOK"))
            continue;

        // tcp retry
        if (!sendCommand("set i f 0x7\r", "AOK"))
            continue;
            
        // set dns server
        if (!sendCommand("set d n rn.microchip.com\r", "AOK"))
            continue;

        //dhcp
        sprintf(cmd, "set i d %d\r", (state.dhcp) ? 1 : 0);
        if (!sendCommand(cmd, "AOK"))
            continue;

        // ssid
        sprintf(cmd, "set w s %s\r", ssid);
        if (!sendCommand(cmd, "AOK", NULL, 1000))
            continue;

        //auth
        sprintf(cmd, "set w a %d\r", state.sec);
        if (!sendCommand(cmd, "AOK"))
            continue;

        // if no dhcp, set ip, netmask and gateway
        if (!state.dhcp) {
            DBG("not dhcp\r");

            sprintf(cmd, "set i a %s\r\n", ip);
            if (!sendCommand(cmd, "AOK"))
                continue;

            sprintf(cmd, "set i n %s\r", netmask);
            if (!sendCommand(cmd, "AOK"))
                continue;

            sprintf(cmd, "set i g %s\r", gateway);
            if (!sendCommand(cmd, "AOK"))
                continue;
        }

        //key step
        if (state.sec != NONE) {
            if ((state.sec == WPA)||(state.sec == WPA2))
                sprintf(cmd, "set w p %s\r", phrase);
            else if (state.sec == WEP_128)
                sprintf(cmd, "set w k %s\r", phrase);

            if (!sendCommand(cmd, "AOK", NULL, 1000))
                continue;
        }

        //join the network
        sprintf(cmd, "join\r");
        if (!sendCommand(cmd, "Associated", NULL, 3000))
            continue;
        
        if (!sendCommand("", "IP=", NULL, 10000))
            continue;

        if (state.dhcp) {
            if (!sendCommand("get i\r", "DHCP=ON", NULL, 3000))
                continue;
        }

        if (!sendCommand("save\r", "Stor"))
            continue;

        exit();

        state.associated = true;
        INFO("ssid: %s", this->ssid);
        INFO("phrase: %s", this->phrase);
        INFO("security: %s", getStringSecurity());

        return true;
    }
    return false;
}


bool Wifly::setProtocol(Protocol p)
{
    // use udp auto pairing
    char cmd[20];
    sprintf(cmd, "set i p %d\r", p);
    if (!sendCommand(cmd, "AOK"))
        return false;

    switch(p) {
        case TCP:
            // set ip flags: tcp retry enabled
            if (!sendCommand("set i f 0x07\r", "AOK"))
                return false;
            break;
        case UDP:
            // set ip flags: udp auto pairing enabled
            if (!sendCommand("set i h 0.0.0.0\r", "AOK"))
                return false;
            if (!sendCommand("set i f 0x40\r", "AOK"))
                return false;
            break;
    }
    state.proto = p;
    return true;
}

char * Wifly::getStringSecurity()
{
    switch(state.sec) {
        case NONE:
            return "NONE";
        case WEP_128:
            return "WEP_128";
        case WPA:
            return "WPA";
        case WPA2:
            return "WPA2";
    }
    return "UNKNOWN";
}

bool Wifly::connect(const char * host, int port)
{
    char rcv[28];
    char cmd[28];
    
    

    // try to open
    sprintf(cmd, "open %s %d\r", host, port);
    DBG("CMD1: %s",cmd);
    if (sendCommand(cmd, "OPEN", rcv, 10000)) {
        state.tcp = true;
        state.cmd_mode = false;
        return true;
    }
    DBG("CMD2: %s",cmd);
    DBG("CMD4: %s",rcv);
    // if failed, retry and parse the response
    if (sendCommand(cmd, NULL, rcv, 5000)) {
        DBG("CMD5: %s",rcv);
        if (strstr(rcv, "OPEN") == NULL) {
            if (strstr(rcv, "Connected") != NULL) {
                wait(0.25);
                if (!sendCommand("close\r", "CLOS"))
                    return false;
                wait(0.25);
                if (!sendCommand(cmd, "OPEN", NULL, 10000))
                    return false;
            } else {
                return false;
            }
        }
    } else {
        DBG("CMD5: %s",rcv);
        return false;
    }
    

    state.tcp = true;
    state.cmd_mode = false;

    return true;
}


bool Wifly::gethostbyname(const char * host, char * ip)
{
    string h = host;
    char cmd[30], rcv[100];
    int l = 0;
    char * point;
    int nb_digits = 0;

    // no dns needed
    int pos = h.find(".");
    if (pos != string::npos) {
        string sub = h.substr(0, h.find("."));
        nb_digits = atoi(sub.c_str());
    }
    if (count(h.begin(), h.end(), '.') == 3 && nb_digits > 0) {
        strcpy(ip, host);
    }
    // dns needed
    else {
        nb_digits = 0;
        sprintf(cmd, "lookup %s\r", host);
        if (!sendCommand(cmd, NULL, rcv))
            return false;

        // look for the ip address
        char * begin = strstr(rcv, "=") + 1;
        for (int i = 0; i < 3; i++) {
            point = strstr(begin + l, ".");
            DBG("str: %s", begin + l);
            l += point - (begin + l) + 1;
        }
        DBG("str: %s", begin + l);
        while(*(begin + l + nb_digits) >= '0' && *(begin + l + nb_digits) <= '9') {
            DBG("digit: %c", *(begin + l + nb_digits));
            nb_digits++;
        }
        memcpy(ip, begin, l + nb_digits);
        ip[l+nb_digits] = 0;
        DBG("ip from dns: %s", ip);
    }
    return true;
}


void Wifly::flush()
{
    buf_wifly.flush();
}

bool Wifly::sendCommand(const char * cmd, const char * ack, char * res, int timeout)
{
    if (!state.cmd_mode) {
        cmdMode();
    }
    if (send(cmd, strlen(cmd), ack, res, timeout) == -1) {
        ERR("sendCommand: cannot (%s)", cmd);
        if(strcmp(cmd, "exit") != 0){
            exit();
        }else{
            WARN("Manually setting cmd_mode to false.");
            state.cmd_mode = false;
        }
        return false;
    }
    return true;
}

bool Wifly::cmdMode()
{
    // if already in cmd mode, return
    if (state.cmd_mode)
        return true;
        
    if (send("$$$", 3, "CMD") == -1 && send("\r",1,">") != true) {
        ERR("cannot enter in cmd mode");
        exit();
        return false;
    }
    state.cmd_mode = true;
    return true;
}

bool Wifly::disconnect()
{
    // if already disconnected, return
    if (!state.associated)
        return true;

    if (!sendCommand("leave\r", "DeAuth"))
        return false;
    exit();

    state.associated = false;
    return true;

}

bool Wifly::is_connected()
{
    return (tcp_status.read() ==  1) ? true : false;
}


void Wifly::reset()
{
    
    reset_pin = 0;
    wifi.baud(9600);
    flush();
    state.cmd_mode = false;
    wait(0.5);
    reset_pin = 1;
    // Set the serial port baud rate back to default:
    wait(0.5);
    INFO("Wifi Shield Reset");
    setBaud(-1);
    DBG("Baud Rate updated from Reset.");
}

bool Wifly::reboot()
{
    // if already in cmd mode, return
    if (!sendCommand("reboot\r"))
        return false;
    
    wait(0.3);

    state.cmd_mode = false;
    return true;
}

bool Wifly::close()
{
    // if not connected, return
    if (!state.tcp)
        return true;

    wait(0.25);
    if (!sendCommand("close\r", "CLOS"))
        return false;
    exit();

    state.tcp = false;
    return true;
}


int Wifly::putc(char c)
{
    while (!wifi.writeable());
    return wifi.putc(c);
}


bool Wifly::exit()
{
    flush();
    if (!state.cmd_mode)
        return true;
    if (!sendCommand("exit\r", "EXIT"))
        return false;
    state.cmd_mode = false;
    flush();
    return true;
}


int Wifly::readable()
{
    return buf_wifly.available();
}

int Wifly::writeable()
{
    return wifi.writeable();
}

char Wifly::getc()
{
    char c;
    //DBG("Waiting for buf_wifly.available() to return true...");
    while (!buf_wifly.available());
    //DBG("Dequeue-ing c...");
    buf_wifly.dequeue(&c);
    //DBG("Return c:(%c)",c);
    return c;
}

void Wifly::handler_rx(void)
{
    //read characters
    while (wifi.readable())
        buf_wifly.queue(wifi.getc());
}

void Wifly::attach_rx(bool callback)
{
    if (!callback)
        wifi.attach(NULL);
    else
        wifi.attach(this, &Wifly::handler_rx);
}


int Wifly::send(const char * str, int len, const char * ACK, char * res, int timeout)
{
    char read;
    size_t found = string::npos;
    string checking;
    Timer tmr;
    int result = 0;
    
    // Don't display the string that we will be sending because it is encoded, thus may screw up printf()
    if(ACK == NULL){
        DBG("Will send a string, no ACK requested.");
    }else{
        DBG("Will send a string, looking for ACK: %s", ACK);
    }

    attach_rx(false);

    //We flush the buffer
    while (wifi.readable())
        wifi.getc();

    if (!ACK || !strcmp(ACK, "NO")) {
        for (int i = 0; i < len; i++)
            result = (putc(str[i]) == str[i]) ? result + 1 : result;
    } else {
        //We flush the buffer
        while (wifi.readable())
            wifi.getc();

        tmr.start();
        for (int i = 0; i < len; i++)
            result = (putc(str[i]) == str[i]) ? result + 1 : result;

        while (1) {
            if (tmr.read_ms() > timeout) {
                //We flush the buffer
                while (wifi.readable())
                    wifi.getc();

                DBG("check: %s", checking.c_str());
                DBG("result ignored: %d", result)
                attach_rx(true);
                return -1;
            } else if (wifi.readable()) {
                read = wifi.getc();
                if ( read != '\r' && read != '\n') {
                    checking += read;
                    found = checking.find(ACK);
                    if (found != string::npos) {
                        wait(0.01);

                        //We flush the buffer
                        while (wifi.readable())
                            wifi.getc();

                        break;
                    }
                } 
            }
        }
        DBG("check: %s", checking.c_str());
        DBG("result: %d", result)
        attach_rx(true);
        return result;
    }

    //the user wants the result from the command (ACK == NULL, res != NULL)
    if ( res != NULL) {
        int i = 0;
        Timer timeout;
        timeout.start();
        tmr.reset();
        while (1) {
            if (timeout.read() > 2) {
                if (i == 0) {
                    res = NULL;
                    break;
                }
                res[i] = '\0';
                DBG("user str 1: %s", res);

                break;
            } else {
                if (tmr.read_ms() > 300) {
                    res[i] = '\0';
                    DBG("user str2: %s", res);

                    break;
                }
                if (wifi.readable()) {
                    tmr.start();
                    read = wifi.getc();

                    // we drop \r and \n
                    if ( read != '\r' && read != '\n') {
                        res[i++] = read;
                    }
                }
            }
        }
        DBG("user str3: %s", res);
    }

    //We flush the buffer
    while (wifi.readable())
        wifi.getc();

    attach_rx(true);
    DBG("result: %d", result)
    return result;
}

int Wifly::checkNetworkStatus(void){
    int status = 0;   
    char rcv[128];
    unsigned int ConnectionReg;
    
    if(!sendCommand("show c\r", NULL, rcv, 5000)){
        status = 0;
        // the sendCommand function quits command mode if there is an error
    }else{
        // check the response:
        sscanf(rcv, "%x%*s", &ConnectionReg);
        if(((ConnectionReg & CC_ASSOCIATION)>0) && ((ConnectionReg & CC_AUTHENTICATION)>0)){
            // Associated and Authenticated:
            status = 3;
        }else if((ConnectionReg & CC_ASSOCIATION)>0){
            // Associated:
            status = 1;
        }else if((ConnectionReg & CC_AUTHENTICATION)>0){
            // Athenticated:
            status = 2;
        }else{
            // Disconnected:
            status = 0;
        }
        INFO("Wifly Check network response: 0x%x, Status: %d", ConnectionReg, status);
        exit(); // Quit command mode
    }
    
    return status;
}

void Wifly::sleep(void){
    // This function puts the module to sleep:
    sendCommand("sleep\r", NULL);
    // It will wake up from sleep if any characters are sent to it.
    return;
}