Pathfindr / Mbed OS mbed-os-PF-UWBBEACON_v1_dev

Dependencies:   aconno_I2C Lis2dh12 WatchdogTimer

modem.cpp

Committer:
pathfindr
Date:
2019-08-09
Revision:
56:efd9f5613549
Parent:
54:2840f2d50734

File content as of revision 56:efd9f5613549:

#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)
{
    //initially setup control lines as no pull to reduce power consumption, they will be correctly configured by .on() and .off()
    nrf_gpio_cfg_input(PN_GSM_PWR_KEY, NRF_GPIO_PIN_NOPULL);
    nrf_gpio_cfg_input(PN_GSM_WAKE_DISABLE, NRF_GPIO_PIN_NOPULL);
}

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::ATwaitForWordOrBO(char* word1, uint32_t timeout)
{
      char* word2 = "RDY\r";
      int targetIndex1 = 0;
      int targetIndex2 = 0;
      bool havefullmatch1 = false;
      bool havefullmatch2 = false;
      char captured[32];
      memset(captured,0,sizeof(captured));
      Timer t;
      t.start();
      uint32_t startmillis = t.read_ms();
      uint32_t runtime = 0;
      while((!havefullmatch1 || !havefullmatch2) && runtime < timeout)
      {
          runtime = (t.read_ms() - startmillis);
          if(NRFuart_readable())
           {
              char c = NRFuart_getc();
              if (c != word1[targetIndex1])
               { //no match, reset
                  targetIndex1 = 0;
              }
              
              if (c != word2[targetIndex2])
               { //no match, reset
                  targetIndex2 = 0;
              }          
              
              if (c == word1[targetIndex1])
               { //we have a match
                  captured[targetIndex1] = c;
                  targetIndex1 ++;
                  //check for full match
                  if (  strcmp(word1, captured) == 0  )
                   {
                      havefullmatch1 = true;
                  }
              }
              
               if (c == word2[targetIndex2])
               { //we have a match
                  captured[targetIndex2] = c;
                  targetIndex2 ++;
                  //check for full match
                  if (  strcmp(word2, captured) == 0  )
                   {
                      havefullmatch2 = true;
                  }
              }
          }
      }
      t.stop();
      t.reset();
      
      //if bool response
      if (havefullmatch1) {
        return true;
      }
      else {
        addToExceptionStringRetainedUntilSuccess("BO");
        return false;
      }
      
    
      //if int response
      if (havefullmatch1) {
            return 1;
      }
      else if(havefullmatch2) {
            return 2;
      }
      else {
            return 0;
      }
}
*/


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.reset();
    t.stop();
    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.input(); // this sets the modem to airplane mode
        _vreg_en = 1;
        ThisThread::sleep_for(200);
        //set prwkey to output low
        _pwrkey.output();
        _pwrkey = 0;
        ThisThread::sleep_for(200);
        //set prwkey back to tri-state
        _pwrkey.input(); 
        
        GLOBAL_modemOn = true;
        
        //CONFIGURE
        if (ATwaitForWord("RDY",ATTIMEOUT_MED)) {
            
            //TURN OFF ECHO
            ATsendCMD("ATE0");
            ATwaitForWord("OK",ATTIMEOUT_SHORT);
            
            //DISABLE LOW POWER WARNING
            //ATsendCMD("AT+QCFG=\"vbatt\",-2,3300,0");
            //ATwaitForWord("OK",ATTIMEOUT_SHORT);
        
            //DISABLE LNA
            ATsendCMD("AT+QGPSCFG=\"lnacontrol\",0");
            ATwaitForWord("OK",ATTIMEOUT_SHORT);
                         
            //ENABLE AIRPLANE MODE CONTROL WITH PIN
            ATsendCMD("AT+QCFG=\"airplanecontrol\",1");
            ATwaitForWord("OK",ATTIMEOUT_SHORT);
            _w_disable.output();
            _w_disable = 0;
                
            if (force2G) {
                ATsendCMD("AT+QCFG=\"nwscanseq\",1,1"); //2G priority
                ATwaitForWord("OK",ATTIMEOUT_SHORT);
                ATsendCMD("AT+QCFG=\"nwscanmode\",1,1"); //2G only connection
                ATwaitForWord("OK",ATTIMEOUT_SHORT);
            } else {
                //PRIORITISE 2G connection (reason being uses less power in some instances and can get cell tower tri)
                //ATsendCMD("AT+QCFG=\"nwscanseq\",1,1"); //2G priority
                //ATsendCMD("AT+QCFG=\"nwscanseq\",2,1"); //3G priority
                ATsendCMD("AT+QCFG=\"nwscanseq\",0,1"); //AUTO - default
                ATwaitForWord("OK",ATTIMEOUT_SHORT);
                ATsendCMD("AT+QCFG=\"nwscanmode\",0,1"); //AUTO
                //ATsendCMD("AT+QCFG=\"nwscanmode\",1,1"); //2G only connection
                //ATsendCMD("AT+QCFG=\"nwscanmode\",2,1"); //3G only connection
                ATwaitForWord("OK",ATTIMEOUT_SHORT);
            }
            return true;
        } else {
            return false;   
        }
    } else {
        return true;   
    }
}

void Modem::off(bool soft) 
{
    if (GLOBAL_modemOn == true) {
        if (soft) {
            bool atok = false;
            int  atoktries = 0;
            while(atok == false && atoktries < 3) {
                ATsendCMD("AT");
                if (ATwaitForWord("OK\r\n",ATTIMEOUT_SHORT)) {
                    atok = true;
                }
                atoktries ++;
            }
            if (atok == true) {
                ATsendCMD("AT+QPOWD=0");
                ATwaitForWord("POWERED DOWN",ATTIMEOUT_SHORT);
            }
        }
        GLOBAL_modemOn = false;
    }
    ThisThread::sleep_for(250);
    GLOBAL_registeredOnNetwork = false;
    _w_disable.input(); //enable airplane mode
    _pwrkey.input();
    _vreg_en = 0;       //kill power to module
    
    //disable control pins, needed to get low power off state
    nrf_gpio_cfg_input(PN_GSM_PWR_KEY, NRF_GPIO_PIN_NOPULL);
    nrf_gpio_cfg_input(PN_GSM_WAKE_DISABLE, NRF_GPIO_PIN_NOPULL);
}

uint64_t Modem::getIMEI() 
{
    uint64_t imei = 0;
    int tries = 0;
    ThisThread::sleep_for(200); //needed
    while(imei == 0 && tries < 3) { //try 3 times
        tries ++;
        ATsendCMD("AT+GSN");
        if (ATwaitForWord("\r\n",ATTIMEOUT_SHORT)) {
            if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
                imei = atoll(ATinBuffer);
            }
        };
        NRFuart_flush();
    }
    return imei;
}

uint64_t Modem::getCCID() 
{
    uint64_t ccid = 0;
    int tries = 0;
    ThisThread::sleep_for(200); //needed
    while(ccid == 0 && tries < 3) { //try 3 times
        tries ++;
        ATsendCMD("AT+QCCID");
        if (ATwaitForWord("+QCCID: ",ATTIMEOUT_SHORT)) {
            if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
                ccid = atoll(ATinBuffer);
            }
        };
        NRFuart_flush();
    }
    return ccid;
}

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, uint32_t &BYREF_NetworkFailCount) 
{
    //CHECK WE ARE NOT ALREADY ON NETOWRK
    if (!GLOBAL_registeredOnNetwork) {
        int attempt = 0;
        Timer t;
        t.start();
        //DISABLE AIRPLANE MODE
        _w_disable.input();
        while (attempt < maxAttempts) {
            t.reset();
            uint32_t startseconds = t.read();
            uint32_t runtime = 0;
            while(GLOBAL_registeredOnNetwork == false && runtime < timeout) {
                runtime = (t.read() - startseconds);
                ThisThread::sleep_for(1000);
                //TURN OFF ECHO //we need this again incase of modem brown out, as it means the modems starts echoing again
                    ATsendCMD("ATE0");
                    ATwaitForWord("OK",ATTIMEOUT_SHORT);
                ATsendCMD("AT+CREG?");
                if (ATwaitForWord("+CREG: 0,5",ATTIMEOUT_VERYSHORT)) {
                    NRFuart_flush();
                    GLOBAL_registeredOnNetwork = true;
                };
                
            }
            attempt ++;
            //if there are more attempt potential, then reset the modem
            if (GLOBAL_registeredOnNetwork == false && attempt < maxAttempts) {
                off(false);
                ThisThread::sleep_for(1000);
                on(false);
            }
        }
        t.reset();
        t.stop();
    }
    NRFuart_flush();
    if (GLOBAL_registeredOnNetwork == true) {        
        BYREF_NetworkFailCount = 0;
        ThisThread::sleep_for(1000); //wait 1 seconds for things to settle
        return true;
    } else {
        BYREF_NetworkFailCount ++;
        return false;   
    }
}

char* Modem::HTTPpost(char* url, char* message, bool getResponse, int maxAttempts) 
{
    bool sent = false;
    bool criticalfail = false;
    int attempt = 0;
    bool gotResponse = false;
    
    //TRY X NUMBER OF TIMES
    while (!sent && attempt < maxAttempts) {
        criticalfail = false;
        
        //TURN OFF ECHO //we need this again incase of modem brown out, as it means the modems starts echoing again
            ATsendCMD("ATE0");
            ATwaitForWord("OK",ATTIMEOUT_SHORT);
        
        if (!criticalfail) {
            ATsendCMD("AT+QHTTPCFG=\"contextid\",1");    
            if (!ATwaitForWord("OK\r",ATTIMEOUT_SHORT)) criticalfail = true;
        }
        
        if (!criticalfail) {
            ATsendCMD("AT+QICSGP=1,1,\"tsiot\",\"\",\"\",0");
            if (!ATwaitForWord("OK\r",ATTIMEOUT_SHORT)) criticalfail = true;
        }
        
        if (!criticalfail) {
            ATsendCMD("AT+QIACT=1");    
            if (!ATwaitForWord("OK\r",ATTIMEOUT_LONG)) criticalfail = true;
        }
        
        /*if (!criticalfail) {
            ATsendCMD("AT+QIACT?");    
            if (!ATwaitForWord("OK\r",ATTIMEOUT_SHORT)) criticalfail = true;
        }*/
        
        if (!criticalfail) {
            char URLcmd[60];
            snprintf(URLcmd, sizeof(URLcmd), "AT+QHTTPURL=%d,80", strlen(url));
            ATsendCMD(URLcmd);
            if (!ATwaitForWord("CONNECT\r",ATTIMEOUT_LONG)) criticalfail = true;
        }
        
        if (!criticalfail) {
            ATsendCMD(url);
            if (!ATwaitForWord("OK\r",ATTIMEOUT_SHORT)) criticalfail = true;
        }
        
        if (!criticalfail) {
            char POSTparams[60];
            snprintf(POSTparams, sizeof(POSTparams), "AT+QHTTPPOST=%d,80,80", strlen(message));
            ATsendCMD(POSTparams);
            if (!ATwaitForWord("CONNECT\r\n",ATTIMEOUT_LONG)) criticalfail = true;
        }
        
        if (!criticalfail) {
            ATsendCMD(message);
            if (ATwaitForWord("+QHTTPPOST: 0",ATTIMEOUT_MED)) {
                sent = true;
            } else {
                criticalfail = true;
            }
        }
        
        if (!criticalfail && getResponse) {
            ATsendCMD("AT+QHTTPREAD=80");
            ATwaitForWord("CONNECT\r\n",ATTIMEOUT_MED);
            ATgetResponse('\r',ATTIMEOUT_SHORT);
            
            if (ATwaitForWord("+QHTTPREAD: 0",ATTIMEOUT_MED)) {
                gotResponse = true;
            }
        }
        
        NRFuart_flush();
        attempt ++;
    }
    if (sent) {
        if (getResponse == false) {
            return "sendonly";
        } else {
            if (gotResponse == true) {
                return ATinBuffer;
            } else {
                return "noresp";
            }
        }
    } else {
        return "err";
    }
}
 
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)) {
            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
                    }
                    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";   
    }
}

/*
bool Modem::USSDsend(char* message) 
{
    bool sent = false;
    int attempt = 0;
    
    //CHECK FOR AT OK
    bool atok = false;
    int  atoktries = 0;
    while(atok == false && atoktries < 3) {
        ATsendCMD("AT");
        if (ATwaitForWord("OK\r\n",ATTIMEOUT_SHORT)) {
            atok = true;
        }
        atoktries ++;
    }
    if (!atok) return false;
    
    char bytestosend[160];
    snprintf(bytestosend, sizeof(bytestosend), "AT+CUSD=1,\"#469*%s#\"", message);
        
    //TRY X NUMBER OF TIMES
    //maxAttempts
    while (!sent && attempt < 10) { //20 seconds, 100*200ms
        //TURN OFF ECHO //we need this again incase of modem brown out, as it means the modems starts echoing again
        ATsendCMD("ATE0");
        ATwaitForWord("OK",ATTIMEOUT_SHORT);
        ATsendCMD(bytestosend);
        if (!ATwaitForWord("+CME ERROR",200)) { 
            if (ATwaitForWord("+CUSD: 0",ATTIMEOUT_SHORT)) {
                sent = true;
            };
        }
        if (sent == false) {
            ThisThread::sleep_for(1000);   
        }
        NRFuart_flush();
        attempt ++;
    }
    
    if (sent) {
        return true;
    } else {
        return false;   
    }
}

char* Modem::USSDreceive(void) 
{
    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)) {
            if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {  
                if ( (matchCount = sscanf(ATinBuffer,",\"%d#%[^#]",USSDmessageIndex,ATinBuffer) ) > 0 ) {
                    received = true;
                }
            }
        }
    }
    NRFuart_flush();
    t.reset();
    t.stop();
    if (received) {
        return ATinBuffer;
    } else {
        return "err";
    }
}


char* Modem::USSDmessage(char* message, bool needResponse, char* api) 
{  
    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)\0",api);
        result = USSDsend(message_failsafe);
    } else {
        result = USSDsend(message);
    }
    if (result) {
        if (needResponse) {
            char* response = USSDreceive();
            if (strcmp(response, "err") != 0) {
                return response;
            } else {
                return "sendokrecfail";
            }
        } else {
            return "sendok";
        } 
    } else {
        return "sendfail";   
    }
}
*/

char* Modem::getLocation(uint8_t accuracy, uint16_t timeout_seconds, uint32_t &BYREF_GPSFailCount, uint32_t &BYREF_NetworkFailCount) 
{ 
    NRFuart_flush();
    bool haveGPSFix = false;
    bool haveCellFix = false;
    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;
    static char locDataOut[70];
    memset(locDataOut,0x00,sizeof(locDataOut));
    Timer t;
    t.start();
    uint32_t startseconds;
    uint32_t runtime;
    
    //TURN OFF ECHO //we need this again incase of modem brown out, as it means the modems starts echoing again
    ATsendCMD("ATE0");
    ATwaitForWord("OK",ATTIMEOUT_SHORT);
    
    if (accuracy >= 2 && BYREF_GPSFailCount <= DEFAULT_MAX_FAILED_GPS) {
        //TURN ON GPS
        ATsendCMD("AT+QGPS=1");
        ATwaitForWord("OK",ATTIMEOUT_SHORT);
        
        //TRY UNTIL TIMEOUT
        uint8_t GPS_fixstage = 0;
        uint8_t GPS_fixloopcount = 0;
        startseconds = t.read();
        runtime = 0;
        
        while(GPS_fixstage < 2 && runtime < timeout_seconds) {
            ThisThread::sleep_for(4000); //this goes first
            runtime = (t.read() - startseconds);
            //TURN OFF ECHO //we need this again incase of modem brown out, as it means the modems starts echoing again
            ATsendCMD("ATE0");
            ATwaitForWord("OK",ATTIMEOUT_SHORT);
            ATsendCMD("AT+QGPSLOC=2");
            if (ATwaitForWord("+QGPSLOC: ",ATTIMEOUT_SHORT)) {
                int matchCount = 0;
                if (ATgetResponse('\r',ATTIMEOUT_VERYSHORT)) { 
                    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”:“2.0",“spd”:“0.0"}                    
                        haveGPSFix = true;
                        GPS_fixstage = 1;
                        GPS_fixloopcount ++;
                        if (accuracy > 2) {
                            if (hdp <= 1.8f) {
                                GPS_fixstage = 2;
                            }
                        } else {
                            if (GPS_fixloopcount > 2) {
                                GPS_fixstage = 2;
                            }
                        }
                    }
                }
            }
        }
        
        if (haveGPSFix) {
            sprintf(locDataOut,",g:(fix:GPS,sat:%d,lat:%.5f,lng:%.5f,hdp:%.0f,spd:%.0f)\0",sat,lat,lng,hdp,spkm);
            memset(GLOBAL_GPSlocString_prev,0x00,sizeof(GLOBAL_GPSlocString_prev));
            memcpy(GLOBAL_GPSlocString_prev,locDataOut,sizeof(GLOBAL_GPSlocString_prev)); //save for future recall
            GLOBAL_have_GPSlocString_prev = true;
            BYREF_GPSFailCount = 0;
        } else {
            BYREF_GPSFailCount ++; 
        }
        
        //TURN OFF GPS
        ATsendCMD("AT+QGPSEND");
        ATwaitForWord("OK",ATTIMEOUT_SHORT);
    }
    
    //SHALL WE GET CELL LOCATION
    if (!haveGPSFix && accuracy >= 1) {
        uint16_t cellLocateTimeout = timeout_seconds;
        if (registerOnNetwork(DEFAULT_CONNECTION_ATTEMPTS,cellLocateTimeout,BYREF_NetworkFailCount)) {
            int matchCount; char type[6]; char cellID[6]; char lac[6]; int mcc; int mnc;
            t.reset();
            startseconds = t.read();
            runtime = 0;
            while(haveCellFix == false && runtime < 20) {
                runtime = (t.read() - startseconds);
                //TURN OFF ECHO //we need this again incase of modem brown out, as it means the modems starts echoing again
                ATsendCMD("ATE0");
                ATwaitForWord("OK",ATTIMEOUT_SHORT);
                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;
                        }
                    }
                }
                ATwaitForWord("OK",ATTIMEOUT_SHORT);
            }
                    
            //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);
        }
    }
    
    t.reset();
    t.stop();
    
    //debug_prep();snprintf(GLOBAL_debug_buffer, sizeof(GLOBAL_debug_buffer), "locdata: %s",locDataOut);debug_exe();

    
    //RETURN
    if (accuracy == 0) {
        sprintf(locDataOut,"\0");
    } else if (!haveGPSFix && !haveCellFix) {
        sprintf(locDataOut,"\0");
    }
    return locDataOut;
}

Modem::~Modem(){};