Pathfindr / Mbed OS mbed-os-PF-UWBBEACON_v1_dev

Dependencies:   aconno_I2C Lis2dh12 WatchdogTimer

modem.cpp

Committer:
pathfindr
Date:
2019-01-21
Revision:
47:cc6d4d0bf897
Parent:
43:7b232f03628f
Child:
48:64b1613941d5

File content as of revision 47:cc6d4d0bf897:

#include "modem.h"

char ATinBuffer[200];

Modem::Modem(PinName pwrkey, PinName vreg_en, PinName w_disable): _pwrkey(pwrkey), _vreg_en(vreg_en), _w_disable(w_disable)
{
}

void Modem::ATsendCMD(char* cmd) 
{ 
    NRFuart_flush();
    NRFuart_puts(cmd);
    NRFuart_puts("\r");
}

bool Modem::ATwaitForWord(char* word, uint32_t timeout) 
{
    int targetIndex = 0;
    bool havefullmatch = false;
    char captured[32];
    memset(captured,0,sizeof(captured));
    Timer t;
    t.start();
    uint32_t startmillis = t.read_ms();
    uint32_t runtime = 0;
    while(!havefullmatch && runtime < timeout) {
        runtime = (t.read_ms() - startmillis);
        if(NRFuart_readable()) { 
            char c = NRFuart_getc();
            if (c != word[targetIndex]) { //no match, reset
                targetIndex = 0; 
            }
            if (c == word[targetIndex]) { //we have a match
                captured[targetIndex] = c;
                targetIndex ++;
                //check for full match
                if (  strcmp(word, captured) == 0  ) {
                    havefullmatch = true;
                }
            }
        }
    }
    t.stop();
    t.reset();
    if (havefullmatch) {
        return true;
    } else {
        return false;
    }
}


bool Modem::ATgetResponse(char terminator, uint32_t timeout) 
{
    memset(ATinBuffer,0x00,sizeof(ATinBuffer));
    int charindex = 0;
    bool gotTerminator = false;
    Timer t;
    t.start();
    uint32_t startmillis = t.read_ms();
    uint32_t runtime = 0;
    while(!gotTerminator && runtime < timeout) {
        runtime = (t.read_ms() - startmillis);
        if(NRFuart_readable()) { 
            char c = NRFuart_getc();
            if (c == terminator) {
                gotTerminator = true;
            } else {
                ATinBuffer[charindex] = c;
                charindex++;
            }
        }
    }
    t.stop();
    t.reset();
    ATinBuffer[charindex] = '\n'; //make sure we end with whitespace lf
    return gotTerminator;
}


bool Modem::on(bool force2G) 
{
    NRFuart_init_nohwfc();
    
    if (!GLOBAL_modemOn) {
        _w_disable = 0; // this sets the modem to airplane mode
        _vreg_en = 1;
        ThisThread::sleep_for(200);
        _pwrkey = 0;
        ThisThread::sleep_for(200);
        _pwrkey = 1;
        
        GLOBAL_modemOn = true;
        
        //CONFIGURE
        if (ATwaitForWord("RDY",ATTIMEOUT_MED)) {
            
            //TURN OFF ECHO
            ATsendCMD("ATE0");
            ATwaitForWord("OK",ATTIMEOUT_SHORT);
             
            //ENABLE AIRPLANE MODE CONTROL WITH PIN
            ATsendCMD("AT+QCFG=\"airplanecontrol\",1");
            ATwaitForWord("OK",ATTIMEOUT_SHORT);
                        
            //CONNECTION TYPE
            //ATsendCMD("AT+QCFG=\"nwscanmode\",1"); //2G only connection
            //ATsendCMD("AT+QCFG=\"nwscanmode\",2"); //3G only connection
            //ATsendCMD("AT+QCFG=\"nwscanmode\",0"); //AUTO
            if (force2G) {
                ATsendCMD("AT+QCFG=\"nwscanmode\",1"); //2G only connection
                ATwaitForWord("OK",ATTIMEOUT_SHORT);
            } else {
                ATsendCMD("AT+QCFG=\"nwscanmode\",0"); //AUTO
                ATwaitForWord("OK",ATTIMEOUT_SHORT);
                //PRIORITISE 2G connection (reason being uses less power in some instances and can get cell tower tirangulation)
                ATsendCMD("AT+QCFG=\"nwscanseq\",1"); //2G priority
                //ATsendCMD("AT+QCFG=\"nwscanseq\",2"); //3G priority
                //ATsendCMD("AT+QCFG=\"nwscanseq\",0"); //AUTO - default
                ATwaitForWord("OK",ATTIMEOUT_SHORT);
            }
            
            return true;
        } else {
            return false;   
        }
    } else {
        return true;   
    }
}

void Modem::off(bool soft) 
{
    if (GLOBAL_modemOn) {
        if (soft) {
            //ATsendCMD("AT+QPOWD=0");
            //ATwaitForWord("POWERED DOWN",ATTIMEOUT_VERYSHORT);
        }
        GLOBAL_modemOn = false;
    }
    GLOBAL_registeredOnNetwork = false;
    _w_disable = 0; //enable airplane mode
    _pwrkey = 0;    //set power key low
    _vreg_en = 0;   //kill power to module
}

long long Modem::getIMEI() 
{
    long long imei = 0;
    ATsendCMD("AT+GSN");
    if (ATwaitForWord("\r",ATTIMEOUT_SHORT)) {
        if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
            imei = atoll(ATinBuffer);
        }
    };
    NRFuart_flush();
    return imei;
}

char* Modem::getModemModel() 
{
    char* modemModel;
    ATsendCMD("AT+GMM");
    if (ATwaitForWord("\r",ATTIMEOUT_SHORT)) {
        if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
            sscanf(ATinBuffer,"%s", modemModel);
        }
    };
    NRFuart_flush();
    return modemModel;
}

bool Modem::registerOnNetwork(int maxAttempts, uint32_t timeout) 
{
    //CHECK WE ARE NOT ALREADY ON NETOWRK
    if (!GLOBAL_registeredOnNetwork) {
        int attempt = 0;
        Timer t;
        t.start();
        //DISABLE AIRPLANE MODE
        _w_disable = 1;
        NRFuart_flush();
        while (attempt < maxAttempts) {
            watchdogKick();
            t.reset();
            uint32_t startmillis = t.read_ms();
            uint32_t runtime = 0;
            while(!GLOBAL_registeredOnNetwork && runtime < timeout) {
                runtime = (t.read_ms() - startmillis);
                ThisThread::sleep_for(1000);
                ATsendCMD("AT+CREG?");
                if (ATwaitForWord("+CREG: 0,5",ATTIMEOUT_VERYSHORT)) {
                    NRFuart_flush();
                    GLOBAL_registeredOnNetwork = true;
                };
            }
            if (!GLOBAL_registeredOnNetwork) {
                off(false);
                ThisThread::sleep_for(1000);
                on(RET_force2G);
            }
            attempt ++;
        }
        t.stop();
    }
    NRFuart_flush();
    if (GLOBAL_registeredOnNetwork) {
        return true;
    } else {
        return false;   
    }
}
 
bool Modem::USSDsend(char* message, int maxAttempts) 
{
    bool sent = false;
    int attempt = 0;
    //TRY X NUMBER OF TIMES
    while (!sent && attempt < maxAttempts) {
        char bytestosend[160];
        snprintf(bytestosend, sizeof(bytestosend), "AT+CUSD=1,\"#469*%s#\"", message);
        ATsendCMD(bytestosend);
        if (ATwaitForWord("+CUSD: 0",ATTIMEOUT_MED)) {
            sent = true;
        };
        NRFuart_flush();
        attempt ++;
    }
    if (sent) {
        return true;
    } else {
        return false;   
    }
}

char* Modem::USSDreceive(int messageIndex) 
{
    bool received = false;
    uint32_t timeout = ATTIMEOUT_LONG;
    int USSDmessageIndex = 0;
    int matchCount = 0;
    Timer t;
    t.start();
    //TRY UNTIL TIMEOUT
    uint32_t startmillis = t.read_ms();
    uint32_t runtime = 0;
    while(!received && runtime < timeout) {
        runtime = (t.read_ms() - startmillis);
        if (ATwaitForWord("+CUSD: 0",ATTIMEOUT_SHORT)) {
            led1 = 0;
            if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {  
                if ( (matchCount = sscanf(ATinBuffer,",\"%d#%[^#]",USSDmessageIndex,ATinBuffer) ) > 0 ) {
                    if (USSDmessageIndex == messageIndex) {
                        //NEED TO GET THIS WORKING SO WE KNOW WE ARE DEALING WITH THE RIGHT MESSAGE
                        //MOVE THE BELOW INTO THIS IF STAEMEBNTS WHEN DONE
                    }
                    led1 = 1;
                    received = true;
                }
            }
        }
    }
    NRFuart_flush();
    if (received) {
        return ATinBuffer;
    } else {
        return "err";
    }
}


char* Modem::USSDmessage(char* message, bool needResponse, int maxAttempts, char* api) 
{  
    uint8_t messageIndex = 1;
    bool result;
    int messageLength = strlen(message);
    if (messageLength > USSD_MAXLENGTH) {
        char message_failsafe[100];
        snprintf(message_failsafe,sizeof(message_failsafe),"(%s,a:error,z:TOOBIG,s:1,c:%d)\0",api,messageIndex);
        result = USSDsend(message_failsafe, maxAttempts);
    } else {
        result = USSDsend(message, maxAttempts);
    }
    if (result) {
        if (needResponse) {
            char* response = USSDreceive(messageIndex);
            if (strcmp(response, "err") != 0) {
                return response;
            } else {
                return "sendonly";
            }
        } else {
            return "ok";   
        } 
    } else {
        return "err";   
    }
}


char* Modem::getLocation(uint8_t accuracy, uint16_t timeout_seconds) 
{ 
    NRFuart_flush();
    bool haveGPSFix = false;
    bool haveCellFix = false;
    static char locDataOut[100];
    memset(locDataOut,0x00,sizeof(locDataOut));
    Timer t;
    t.start();
    uint32_t startmillis;
    uint32_t runtime;
        
    if (accuracy >= 2) {
        //Enable External LNA power - IS DISABLED BY DEFAULT
        ATsendCMD("AT+QGPSCFG=\"lnacontrol\",1");
        ATwaitForWord("OK",5000);
        
        //TURN ON GPS
        ATsendCMD("AT+QGPS=1");
        ATwaitForWord("OK",ATTIMEOUT_SHORT);
        
        //TRY UNTIL TIMEOUT
        uint8_t GPS_fixstage = 0;
        uint8_t GPS_fixcount = 0;
        startmillis = t.read_ms();
        runtime = 0;
        while(!haveGPSFix && runtime < (timeout_seconds*1000)) {
            watchdogKick();
            ThisThread::sleep_for(5000); //this goes first
            runtime = (t.read_ms() - startmillis);
            ATsendCMD("AT+QGPSLOC=2");
            if (ATwaitForWord("+QGPSLOC: ",ATTIMEOUT_SHORT)) {
                GPS_fixstage = 1;
                GPS_fixcount ++;
                if (GPS_fixcount == 2) { //wait 10 seconds to get a better fix // need to improve this logic
                    haveGPSFix = true;
                } else {
                    NRFuart_flush();   
                }
            } else {
                NRFuart_flush();  
            }
            if (haveGPSFix) {
                //+QGPSLOC: 233510.0,52.55415,1.24021,1.2,59.2,2,0.00,0.0,0.0,201218,05
                int matchCount = 0;
                float utc;
                float lat;
                float lng;
                float hdp;
                float alt;
                uint8_t fix;
                float cog;
                float spkm;
                float spkn;
                uint32_t date;
                uint8_t sat;
                
                //Example data
                //115757.0,52.62091,1.29536,0.8,58.2,2,0.00,0.0,0.0,211218,07
                if (ATgetResponse('\r',ATTIMEOUT_SHORT)) { 
                    if ((matchCount = sscanf(ATinBuffer,"%f,%f,%f,%f,%f,%d,%f,%f,%f,%d,%d",&utc,&lat,&lng,&hdp,&alt,&fix,&cog,&spkm,&spkn,&date,&sat)) == 11 ) {
                        //{“fix”:“GPS”,“sat”:“9",“lat”:“52.913254",“lng”:“-1.455289",“hdp”:“89.0",“spd”:“0.0"}                    
                        sprintf(locDataOut,",g:(fix:GPS,sat:%d,lat:%.6f,lng:%.6f,hdp:%.1f,spd:%.1f)\0",sat,lat,lng,hdp,spkm);
                    }
                }
            }
        }
        //TURN OFF GPS
        ATsendCMD("AT+QGPSEND");
        ATwaitForWord("OK",ATTIMEOUT_SHORT);
    }
    
    //SHALL WE GET CELL LOCATION
    if (!haveGPSFix && accuracy >= 1) {
        registerOnNetwork(2, 90000);
        int matchCount;
        char type[6];
        char cellID[6];
        char lac[6];
        int mcc;
        int mnc;
        
        startmillis = t.read_ms();
        runtime = 0;
        while(!haveCellFix && runtime < 15000) {
            runtime = (t.read_ms() - startmillis);
            ATsendCMD("AT+QENG=\"servingcell\"");
            if (ATwaitForWord("+QENG: \"servingcell\",\"NOCONN\",",ATTIMEOUT_VERYSHORT)) {
                if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
                    if ((matchCount = sscanf(ATinBuffer,"\"%[^\"]\",%d,%d,%[^,],%[^,]",&type,&mcc,&mnc,&lac,&cellID)) == 5 ) {
                        sprintf(locDataOut,",h:%s.%s.%d.%d\0",cellID,lac,mcc,mnc);
                        //sprintf(locDataOut,",h:41806.2252.234.30\0");
                        haveCellFix = true;
                    }
                }
            }
        }
                
        //example from mulbs
        /*
        2g
        +QENG: "servingcell","NOCONN","2G",234,30,8CC,A34E,20,668,0,-80,0,5,4,26,26,1,-,-,-,-,-,-,-,-,-,"-"
        3g
        +QENG: "servingcell","NOCONN","3G",234,20,8A,CE735F,10588,52,-97,-99,11,25,16,128,-,-,-,-,-,"-",-
        
        +QENG: "neighbourcell","2G",234,30,8CC,A34D,20,656,-89,17,17,0,0
        +QENG: "neighbourcell","2G",234,30,8CC,678,61,686,-104,2,2,0,0
        +QENG: "neighbourcell","2G",234,30,8CC,4303,32,676,-104,2,2,0,0
        +QENG: "neighbourcell","2G",234,30,8CC,B2B2,16,692,-107,-1,-1,0,0
        https://www.neilson.co.za/mobile-network-geolocation-obtaining-the-cell-ids-the-signal-strength-of-surrounding-towers-from-a-gsm-modem/
        */
        
        //ATsendCMD("AT+QENG=\"neighbourcell\"");
        //ATwaitForWord("OK",ATTIMEOUT_LONG);
    }
    
    //RETURN
    if (accuracy == 0) {
        sprintf(locDataOut,"\0");
    } else if (!haveGPSFix && !haveCellFix) {
        sprintf(locDataOut,"\0");
    }
    return locDataOut;
}

Modem::~Modem(){};