Host library for controlling a WiConnect enabled Wi-Fi module.

Dependents:   wiconnect-ota_example wiconnect-web_setup_example wiconnect-test-console wiconnect-tcp_server_example ... more

Wiconnect.cpp

Committer:
aymangrais
Date:
2015-09-28
Revision:
42:8ffb253b09e7
Parent:
37:5ee74d72efe4

File content as of revision 42:8ffb253b09e7:

/**
 * ACKme WiConnect Host Library is licensed under the BSD licence: 
 * 
 * Copyright (c)2014 ACKme Networks.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution. 
 * 3. The name of the author may not be used to endorse or promote products 
 * derived from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include "api/WiconnectInterface.h"
#include "internal/common.h"
#include "internal/CommandCommon.h"




using namespace wiconnect;





#ifdef WICONNECT_ENABLE_MALLOC
#define MALLOC_ARGS , void* (*mallocPtr)(size_t), void (*freePtr)(void*)
#define MALLOC_CONSTRUCTORS   _malloc(mallocPtr), _free(freePtr),
#else
#define MALLOC_ARGS
#define MALLOC_CONSTRUCTORS
#endif


static Wiconnect* instance = NULL;




/*************************************************************************************************/
void Wiconnect::prepare(void *internalBuffer_, int internalBufferSize_, bool nonBlocking_)
{
    instance = this;
    internalBufferAlloc = false;
#ifdef WICONNECT_ENABLE_MALLOC
    if(internalBufferSize_ > 0 && internalBuffer_ == NULL)
    {
        wiconnect_assert(this, "Wiconnect(), malloc not defined", _malloc != NULL);
        internalBuffer = (char*)_malloc(internalBufferSize_);
        internalBufferAlloc = true;
    }
    else
#endif
    {
        internalBuffer = (char*)internalBuffer_;
    }

    internalProcessingState = 0;
    currentCommandId = NULL;
    internalBufferSize = internalBufferSize_;;
    nonBlocking = nonBlocking_;
    commandExecuting = false;
    initialized = false;
    needUpdate = false;
    pinToGpioMapper = NULL;
    defaultTimeoutMs = WICONNECT_DEFAULT_TIMEOUT;

    memset(commandContext, 0, sizeof(commandContext));

#ifdef WICONNECT_ASYNC_TIMER_ENABLED
    commandProcessingPeriod = WICONNECT_DEFAULT_COMMAND_PROCESSING_PERIOD;
    currentQueuedCommand = NULL;
#endif
}


/*************************************************************************************************/
Wiconnect::Wiconnect(const SerialConfig &serialConfig, int internalBufferSize, void *internalBuffer, Pin reset, Pin wake, bool nonBlocking MALLOC_ARGS) :
        NetworkInterface(this), SocketInterface(this), FileInterface(this), GhmInterface(this),
        MALLOC_CONSTRUCTORS serial(serialConfig, this), resetGpio(reset), wakeGpio(wake)
{
#ifdef WICONNECT_ENABLE_MALLOC
    wiconnect_assert(this, "Wiconnect(), bad malloc/free", (mallocPtr == NULL && freePtr == NULL) || (mallocPtr != NULL && freePtr != NULL));
#endif
    prepare(internalBuffer, internalBufferSize, nonBlocking);
}

/*************************************************************************************************/
Wiconnect::Wiconnect(const SerialConfig &serialConfig, Pin reset, Pin wake, bool nonBlocking MALLOC_ARGS) :
    NetworkInterface(this), SocketInterface(this), FileInterface(this), GhmInterface(this),
    MALLOC_CONSTRUCTORS serial(serialConfig, this), resetGpio(reset), wakeGpio(wake)
{
#ifdef WICONNECT_ENABLE_MALLOC
    wiconnect_assert(this, "Wiconnect(), bad malloc/free", (mallocPtr == NULL && freePtr == NULL) || (mallocPtr != NULL && freePtr != NULL));
#endif
    prepare(NULL, 0, nonBlocking);
}

/*************************************************************************************************/
Wiconnect::~Wiconnect()
{
#ifdef WICONNECT_ENABLE_MALLOC
    if(internalBufferAlloc)
    {
        _free(internalBuffer);
    }
#endif
}

/*************************************************************************************************/
WiconnectResult Wiconnect::init(bool bringNetworkUp)
{
    WiconnectResult result;
    int retries;
    bool configuredBus = false;
    bool savedNonBlocking = nonBlocking;

    DEBUG_INFO("Initializing wiconnect");

    serial.initialize();

    if(WICONNECT_FAILED(result, reset()))
    {
        return result;
    }

    delayMs(1000);

    initialized = true;
    nonBlocking = false;


    loop:
    for(retries = 3; retries > 0; --retries)
    {
        result = sendCommand(1000, CMD_SET_SYSTEM_COMMAND_MODE, "machine");
        if(result != WICONNECT_SUCCESS)
        {
            delayMs(100);
        }
        else
        {
            break;
        }
    }

    if(result != WICONNECT_SUCCESS && !configuredBus)
    {
        configuredBus = true;
        if(configureModuleDataBus())
        {
            goto loop;
        }
    }

    if(result == WICONNECT_SUCCESS)
    {
        if(WICONNECT_SUCCEEDED(result, getVersion()))
        {
            const uint32_t version = Wiconnect::wiconnectVersionToInt(this->internalBuffer);
            if(version < WICONNECT_MINIMUM_VERSION)
            {
                needUpdate = true;
                result = WICONNECT_FIRMWARE_OUTDATED;
            }
        }
    }
    if(result == WICONNECT_SUCCESS)
    {
        sendCommand("set stream.auto_close 0");
    }
    if(result == WICONNECT_SUCCESS && bringNetworkUp)
    {
        sendCommand(15000, "ping -g");
    }

    nonBlocking = savedNonBlocking;
    if(result != WICONNECT_SUCCESS && !needUpdate)
    {
        initialized = false;
    }


    return result;
}

/*************************************************************************************************/
void Wiconnect::deinit(void)
{
    initialized = false;
}

/*************************************************************************************************/
Wiconnect* Wiconnect::getInstance()
{
    return instance;
}

/*************************************************************************************************/
bool Wiconnect::isInitialized()
{
    return initialized;
}

/*************************************************************************************************/
bool Wiconnect::updateRequired()
{
    return needUpdate;
}

/*************************************************************************************************/
WiconnectResult Wiconnect::reset()
{
    resetGpio = 0;
    delayMs(10);
    resetGpio = 1;
    delayMs(1000);
    return WICONNECT_SUCCESS;
}

/*************************************************************************************************/
WiconnectResult Wiconnect::wakeup()
{
    wakeGpio = 1;
    delayMs(1);
    wakeGpio = 0;
    return WICONNECT_SUCCESS;
}

/*************************************************************************************************/
void Wiconnect::flush(int delayMs)
{
    if(delayMs != 0)
    {
        serial.write("\r\n\r\n", 4, 0);
    }
    delayMs(delayMs);
    serial.flush();
}

/*************************************************************************************************/
void Wiconnect::setPinToGpioMapper(PinToGpioMapper mapper)
{
    pinToGpioMapper = mapper;
}

/*************************************************************************************************/
WiconnectResult Wiconnect::getVersion(char *versionBuffer, int versionBufferSize, const Callback &completeCallback)
{
    WiconnectResult result;

    if(versionBuffer != NULL && versionBufferSize == 0)
    {
        return WICONNECT_BAD_ARG;
    }

    _CHECK_OTHER_COMMAND_EXECUTING();

    if(versionBuffer == NULL)
    {
        result = sendCommand(completeCallback, CMD_GET_VERSION);
    }
    else
    {
        result = sendCommand(completeCallback, versionBuffer, versionBufferSize, CMD_GET_VERSION);
    }

    _CHECK_CLEANUP_COMMAND();

    return result;
}

/*************************************************************************************************/
WiconnectResult Wiconnect::updateFirmware(bool forced, const char *versionStr, const Callback &completeCallback)
{
    WiconnectResult result;
    char *cmdBuffer = internalBuffer;

    if(_WICONNECT_IS_IDLE())
    {
        strcpy(cmdBuffer, "ota ");
        if(versionStr != NULL)
        {
            strcat(cmdBuffer, "-b wiconnect-");
            strcat(cmdBuffer, versionStr);
        }
        else if(forced)
        {
            strcat(cmdBuffer, "-f");
        }
    }

    _CHECK_OTHER_COMMAND_EXECUTING();

    result = sendCommand(completeCallback, WICONNECT_FIRMWARE_UPDATE_TIMEOUT, cmdBuffer);

    _CHECK_CLEANUP_COMMAND();

    return result;
}

/*************************************************************************************************/
const char* Wiconnect::getWiconnectResultStr(WiconnectResult wiconnectResult)
{
    static const char* const wiconnectSuccessStrTable[] = {
            "Success",                              // WICONNECT_SUCCESS
            "Processing command",                   // WICONNECT_PROCESSING
            "Idle",                                 // WICONNECT_IDLE
            "Aborted",                              // WICONNECT_ABORTED
    };
    static const char* const wiconnectErrorStrTable[] = {
            "",
            "General error",                        // WICONNECT_ERROR
            "WiConnect command code error",         // WICONNECT_CMD_RESPONSE_ERROR
            "Null buffer",                          // WICONNECT_NULL_BUFFER
            "Not initialized",                      // WICONNECT_NOT_INITIALIZED
            "Overflow",                             // WICONNECT_OVERFLOW
            "Timeout",                              // WICONNECT_TIMEOUT
            "Response handler null",                // WICONNECT_RESPONSE_HANDLER_NULL
            "Response parse error",                 // WICONNECT_RESPONSE_PARSE_ERROR
            "Another command is executing",         // WICONNECT_ANOTHER_CMD_EXECUTING
            "Bad argument(s)",                      // WICONNECT_BAD_ARG
            "Unsupported",                          // WICONNECT_UNSUPPORTED
            "Pin name to GPIO mapper null",         // WICONNECT_PINNAME_TO_GPIO_MAPPER_NULL
            "Duplicate",                            // WICONNECT_DUPLICATE
            "Not found",                            // WICONNECT_NOT_FOUND
            "No mapping for pinname to GPIO",       // WICONNECT_PINNAME_TO_GPIO_NO_MAPPING
            "Not connected",                        // WICONNECT_NOT_CONNECTED
            "Underflow",                            // WICONNECT_UNDERFLOW
            "A monitor is not available",           // WICONNECT_MONITOR_NOT_AVAILABLE
            "Not opened for reading",               // WICONNECT_NOT_OPENED_FOR_READING
            "WiFi firmware update required",        // WICONNECT_FIRMWARE_OUTDATED
    };

    if((int)wiconnectResult >= (int)WICONNECT_SUCCESS)
    {
        return wiconnectSuccessStrTable[wiconnectResult];
    }
    else
    {
        wiconnectResult = (WiconnectResult)(-((int)wiconnectResult));
        return wiconnectErrorStrTable[wiconnectResult];
    }
}

/*************************************************************************************************/
uint32_t Wiconnect::wiconnectVersionToInt(char *versionStr)
{
    char *idx = strchr(versionStr, ',');
    if(idx == NULL)
    {
        return UINT_MAX;
    }
    *idx = 0;
    idx = versionStr;
    while((idx = strchr(versionStr, '-')) != NULL)
    {
        versionStr = idx + 1;
    }

    uint32_t ver;

    if(Wiconnect::fileVersionStrToInt(versionStr, &ver))
    {
        return ver;
    }

    return UINT_MAX;
}

/*************************************************************************************************/
void Wiconnect::setDebugLogger(LogFunc logFunc)
{
#ifdef WICONNECT_ENABLE_DEBUGGING
    debugLogger = logFunc;
#endif
}

/*************************************************************************************************/
void Wiconnect::setAssertLogger(LogFunc assertLogFunc)
{
    assertLogger = assertLogFunc;
}

#ifdef WICONNECT_ENABLE_DEBUGGING
/*************************************************************************************************/
void Wiconnect::debugLog(const char *msg, ...)
{
    if(!debugLogger.isValid())
    {
        return;
    }

    static char buffer[96];
    va_list args;
    va_start(args, msg);
    int len = vsnprintf(buffer, sizeof(buffer)-1, msg, args);
    va_end(args);

    if(len > (int)(sizeof(buffer)-6))
    {
        char *p = &buffer[sizeof(buffer)-6];
        *p++ = '.';
        *p++ = '.';
        *p++ = '.';
        *p++ = '\r';
        *p++ = '\n';
        *p = 0;
    }
    else
    {
        if(buffer[len-2] != '\r')
        {
            char *p = &buffer[len];
            *p++ = '\r';
            *p++ = '\n';
            *p = 0;
        }
    }
    debugLogger.call(buffer);
}
#endif