RN41/42 control lib. RN41/42 is bluetooth module with class 1 or 2.

Dependencies:   StrLib myTimer RingBuffer

Dependents:   Theremin

RN41.cpp

Committer:
AkinoriHashimoto
Date:
2015-11-20
Revision:
5:63925964cbd5
Parent:
4:8b74db22e4c6

File content as of revision 5:63925964cbd5:

#include "RN41.h"
//#include "SerialBase.h"

const int RN41::NG=                   0;
const int RN41::OK=                   1;

// PRIVATE
const int RN41::findCmp12=          100;
const int RN41::findCmp1=           101;
const int RN41::findCmp2=           102;

// PUBLIC
const int RN41::ERR_Timeout=        201;
const int RN41::ERR_AddrUnder12=    210;
const int RN41::ERR_AddrOver12=     211;
const int RN41::ERR_Connect=        220;
const int RN41::FAIL_Connect=       221;
const int RN41::ERR_EnterCmdMode=   230;
const int RN41::ERR_Disconnect=     240;

const int RN41::ERR_NameSize=       310;

/*
const int RN41::None=                10;
const int RN41::Odd=                 11;
const int RN41::Even=                12;
*/



RN41::RN41(PinName TX, PinName RX, int baudrate, int bit, int parity, int stop, bool CRLN)
    : rn41(TX, RX)
{
    this->setDev_UART(baudrate, bit, parity, stop);

    // attach rxIrq. because, rx buf of serial equals to 16 Bytes.
    this->rn41.attach(this, &RN41::_readIrq, Serial::RxIrq);

    cr= '\r';
    if(CRLN)
        cr= "\r\n";
}

int RN41::connect(string addr)
{
    addr= toAlpanumeric(addr, true);
    int idx= addr.size();
    // ex. 0006:66:72:6e05\r ->000666726e05.
    if(idx < 12)
        return ERR_AddrUnder12;
    if(idx > 12)
        return ERR_AddrOver12;

    if(!enterCMD())
        return ERR_EnterCmdMode;       // CMD入れず
    if(!disconnect())
        return ERR_Disconnect;
    if(!enterCMD())
        return ERR_EnterCmdMode;       // CMD入れず

    sendCMD("C,"+ addr);
    bool2 find;
    find= chkReply("TRYING\r", 1000, "ERR\r");    // within waiting for Reply

    // TRYINGが返って来ない場合
    if(!find.b1)
        return ERR_Connect;

    // Trying to connect to remote Bluetooth dev.
    find= chkReply("CONNECT,", 10000, "CONNECT failed\r");    // wait 10s(max)

    if(find.b1)
        return OK;
    if(find.b2)
        return FAIL_Connect;

    return ERR_Timeout;
}

string RN41::read()
{
    _read();
    if(rxStrStored.empty() && rxStr.empty())
        return "";

    string tmp= rxStrStored+ rxStr;
//    string tmp= rxStrStored+ ringBuf.get();
    rxStr.erase();
    rxStrStored.erase();
    return tmp;
}

string RN41::getLine()
{
    _read();

    // CRまでを返す
    if(rxStrStored.empty() && rxStr.empty())
        return "";

    int idx= rxStr.rfind('\r');
    if(idx != string::npos) {
        // CR存在
        if(rxStr[++idx] == '\n')    // LF
            idx++;
        rxStrStored += rxStr.substr(0, idx);  // size
        rxStr= rxStr.substr(idx);             // idx
    }
    // rxStrはCRなし、rxStrStoredに調整済み
    string tmp= rxStrStored;
    rxStrStored.erase();
    return tmp;
}

int RN41::setDev_UART(int baud, int bit, int parity, int stop)
{
    rn41.baud(baud);

    // 力技
    if      (parity == SerialBase::Odd)
        rn41.format(bit, SerialBase::Odd,  stop);
    else if (parity == SerialBase::Even)
        rn41.format(bit, SerialBase::Even, stop);     // 8bit, NonParity, 1stopbit
    else if (parity == SerialBase::None)
        rn41.format(bit, SerialBase::None, stop);     // 8bit, NonParity, 1stopbit
    else
        return NG;

    // 9,600bps -> 1,200B/s -> 833us/B. 0.1文字×13回待ちする(安全率30%)。
//    usWait4readBuf= (int)( (float)800000 / (float)baud );
//    if(usWait4readBuf < 1)usWait4readBuf= 1;

    return OK;
}


int RN41::setDev_Name(string str, bool usingAddr)
{
    int strSize= str.size();
    if((strSize==0) || (20<=strSize) || (usingAddr && 15<=strSize))
        return ERR_NameSize;

    // 特殊文字チェックは不要っぽい

    string cmd;
    if(usingAddr)
        cmd= "S-,";
    else
        cmd= "SN,";

    if(!enterCMD())
        return ERR_EnterCmdMode;       // CMD入れず

    sendCMD(cmd+ str);
    if(chkReply("AOK\r", 1000).b1)
        return OK;

    return NG;
}

bool RN41::reboot()
{
    if(!enterCMD())
        return ;       // CMD入れず
    sendCMD("R,1");     

//    bool2 find= chkReply("Reboot!\r", 100);

    return chkReply("Reboot!\r", 100).b1;
}


void RN41::sendCMD(string str, bool addCR)
{
    if(addCR)
        str += '\r';
    rn41.printf(str.c_str());
    return;
}
void RN41::sendLine(string str, bool addCR)
{
    if(addCR)
        str += "\r\n";
    rn41.printf(str.c_str());
    return;
}

bool RN41::enterCMD()
{
    sendCMD("$$$", false);
    if(chkReply("CMD\r", 500).b1)    // 500ms
        return true;

    sendCMD("\r", false);           // 既にCMDモード
    if(chkReply("?\r", 500).b1)
        return true;

    return false;
}

bool RN41::disconnect()
{
    if(!enterCMD())
        return false;       // CMD入れず
    sendCMD("K,");     // rtn KILL ? / ERR ?

    bool2 find;
    find= chkReply("KILL\r", 500, "ERR\r");
    if(find.b2) {    // 既に切断されている
        sendCMD("---");        // exit CMD mode.
        return chkReply("END\r", 500).b1;
//            return true;else// {            leds.ON(4);return false;   // どういう状況か不明
    }
    // 切断中
    return chkReply("DISCONNECT", 5000, "", false).b1;  // CR無いかも?
}


RN41::bool2 RN41::chkReply(string cmp1, int timeout, string cmp2, bool wCR) // withCR
{
    // BT外側から文字列が到着しないと仮定。
    // 目的単語は、取り逃していないと仮定。

    if(rxStr[0] == '\n')
        rxStr= rxStr.substr(1);

    timer.start(true);      // for Timeout
    int idxCR;//, idx1, idx2;//, idxCut;
//    bool2 find;//1, find2;
    bool2 find, tmpFind;
    string tmpStr;


    find.b1= find.b2= false;    // rxStr全てを確認するために、trueのみ上書きしていく。
    while(true) {
        _read();
        // rxStrを確認する。
        idxCR= rxStr.find('\r');
        
        if(idxCR != string::npos) {     // CR found.
            // 通常通信、目的語(wCR)、目的語(woCR)+通常通信。の3パターンのみのはず。
            tmpFind= chkReply_process_wCR(cmp1, cmp2);
            if(tmpFind.b1)
                find.b1= true;
            if(tmpFind.b2)
                find.b2= true;
            // CRがrxStrになくなるまで繰り返す。
        } else {
            // CRなし
            tmpFind= chkReply_process_woCR(cmp1, cmp2, wCR);
            // この時点で、tmpとfindの両方がTrueの場合、目的のリプが2回出てきた。おかしい。
            // とりあ、そのケースは無視。
            if(tmpFind.b1)
                find.b1= true;
            if(tmpFind.b2)
                find.b2= true;

            bool flagRtn= false;
            if(find.b1||find.b2)
                flagRtn= true;

            if(wCR && (tmpFind.b1||tmpFind.b2))
                flagRtn= false;     // CRが出てくるまで待つ

            if(flagRtn)
                return find;
        }

        if(timer.read_ms(false, false, false) > timeout)
            return find;
        wait_ms(1);     // 1ms waiting.
    }   // end while
}

RN41::bool2 RN41::chkReply_process_wCR(string cmp1, string cmp2)
{
    int idx= rxStr.find('\r');
    if(rxStr[++idx] == '\n')
        idx++;
    // rxStr[idx]は次の文字列のindex

    // tmpに切り取って確認する。
    string tmpStr= rxStr.substr(0, idx); // substr(idx, count)
    rxStr=  rxStr.substr(idx);

    b2i2 tmpFind= chkReply_find(tmpStr, cmp1, cmp2);

    if(!(tmpFind.b1 || tmpFind.b2))     // 通常通信
        rxStrStored += tmpStr;
    // cmpと合致していれば、保存しておく必要性がない

/*    if(tmpFind.b1)
        rtn.b1= true;
    if(tmpFind.b2)
        rtn.b2= true;*/    
    bool2 rtn;
    rtn.b1= tmpFind.b1;
    rtn.b2= tmpFind.b2;
    return rtn;
}
RN41::bool2 RN41::chkReply_process_woCR(string cmp1, string cmp2, bool wCR)
{
    b2i2 tmpFind= chkReply_find(rxStr, cmp1, cmp2);
// この時点で、tmpとfindの両方がTrueの場合、目的のリプが2回出てきた。おかしい。
// とりあ、そのケースは無視。

    // 文字列rxStr処理
    if(!wCR && (tmpFind.b1||tmpFind.b2)) {
        int idx1= 0, idx2= 0;
        if(tmpFind.b1)
            idx1= tmpFind.i1+ cmp1.size();
        if(tmpFind.b2)
            idx2= tmpFind.i2+ cmp2.size();

        if(idx1 < idx2)
            idx1= idx2;
        if(idx1 > 0)
            rxStr= rxStr.substr(idx1);
    }
    
    bool2 rtn;
    rtn.b1= tmpFind.b1;
    rtn.b2= tmpFind.b2;
    return rtn;
}
RN41::b2i2 RN41::chkReply_find(string trg, string cmp1, string cmp2)   //, bool wCR) // withCR
{
    b2i2 tmp;
    tmp.b1= tmp.b2= false;
    tmp.i1= tmp.i2= string::npos;

    tmp.i1= trg.find(cmp1);
    if(    (tmp.i1= trg.find(cmp1)) != string::npos)
        tmp.b1= true;
    if(!cmp2.empty())
        if((tmp.i2= trg.find(cmp2)) != string::npos)
            tmp.b2= true;
    return tmp;
}


void RN41::_readIrq(void)
{
    while(rn41.readable())
        ringBuf.set((char)rn41.getc());
    return;
}
void RN41::_read()
{
    // Bufferを吸い尽くす
    this->_readIrq();   // ringBufにSerialバッファからコピーされた。
    rxStr += ringBuf.get();
    return;
}

// EOF