u-blox USB modems (GSM and CDMA)

Dependencies:   CellularUSBModem

Dependents:   C027_CANInterfaceComm C027_ModemTransparentUSBCDC_revb UbloxModemHTTPClientTest C027_HTTPClientTest ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers UbloxModem.cpp Source File

UbloxModem.cpp

00001 /* UbloxModem.cpp */
00002 /* Copyright (C) 2012 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019 
00020 #define __DEBUG__ 3
00021 #ifndef __MODULE__
00022 #define __MODULE__ "UbloxModem.cpp"
00023 #endif
00024 
00025 #include "core/fwk.h"
00026 #include "sms/GSMSMSInterface.h"
00027 #include "sms/CDMASMSInterface.h"
00028 
00029 #include "UbloxModem.h"
00030 
00031 UbloxModem::UbloxModem(IOStream* atStream, IOStream* pppStream) :
00032    m_at(atStream),                          // Construct ATCommandsInterface with the AT serial channel
00033    m_CdmaSms(&m_at),                         // Construct SMSInterface with the ATCommandsInterface
00034    m_GsmSms(&m_at),                          // Construct SMSInterface with the ATCommandsInterface
00035    m_ussd(&m_at),                           // Construct USSDInterface with the ATCommandsInterface
00036    m_linkMonitor(&m_at),                    // Construct LinkMonitor with the ATCommandsInterface
00037    m_ppp(pppStream ? pppStream : atStream),    // Construct PPPIPInterface with the PPP serial channel
00038    m_ipInit(false),                         // PPIPInterface connection is initially down
00039    m_smsInit(false),                        // SMSInterface starts un-initialised
00040    m_ussdInit(false),                       // USSDInterface starts un-initialised 
00041    m_linkMonitorInit(false),                // LinkMonitor subsystem starts un-initialised
00042    m_atOpen(false),                          // ATCommandsInterface starts in a closed state
00043    m_onePort(pppStream == NULL),
00044    m_type(UNKNOWN)
00045 {
00046 }
00047 
00048 
00049 genericAtProcessor::genericAtProcessor()
00050 { 
00051     i = 0; 
00052     str[0] = '\0'; 
00053 }
00054 
00055 const char* genericAtProcessor::getResponse(void) 
00056 { 
00057     return str; 
00058 }
00059 
00060 int genericAtProcessor::onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
00061 {
00062     int l = strlen(line);
00063     if (i + l + 2 > sizeof(str))
00064         return NET_OVERFLOW;
00065     if (i) str[i++] = ',';
00066     strcat(&str[i], line);
00067     i += l;
00068     return OK;
00069 }
00070 
00071 int genericAtProcessor::onNewEntryPrompt(ATCommandsInterface* pInst)
00072 {
00073     return OK;
00074 }
00075 
00076 class CREGProcessor : public IATCommandsProcessor
00077 {
00078 public:
00079   CREGProcessor(bool gsm) : status(STATUS_REGISTERING)
00080   {
00081     m_gsm = gsm;
00082   }
00083   enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
00084   REGISTERING_STATUS getStatus()
00085   {
00086     return status;
00087   }
00088   const char* getAtCommand()
00089   {
00090       return m_gsm ? "AT+CREG?" : "AT+CSS?";
00091   }
00092 private:
00093   virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
00094   {
00095     int r;
00096     if (m_gsm)
00097     {
00098         if( sscanf(line, "+CREG: %*d,%d", &r) == 1 )
00099         {
00100           status = (r == 1 || r == 5) ? STATUS_OK : 
00101                    (r == 0 || r == 2) ? STATUS_REGISTERING :
00102           //       (r == 3)           ? STATUS_FAILED :
00103                                         STATUS_FAILED;
00104         }
00105     }
00106     else
00107     {
00108         char bc[3] = "";
00109         if(sscanf(line, "%*s %*c,%2s,%*d",bc)==1)
00110         {
00111             status = (strcmp("Z", bc) == 0) ? STATUS_REGISTERING : STATUS_OK;
00112         }
00113     }
00114     return OK;
00115   }
00116   virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
00117   {
00118     return OK;
00119   }
00120   volatile REGISTERING_STATUS status;
00121   bool m_gsm;
00122 };
00123 
00124 int UbloxModem::connect(const char* apn, const char* user, const char* password)
00125 {
00126   if( !m_ipInit )
00127   {
00128     m_ipInit = true;
00129     m_ppp.init();
00130   }
00131   m_ppp.setup(user, password, (m_type != LISA_C200) ? DEFAULT_MSISDN_GSM : DEFAULT_MSISDN_CDMA);
00132 
00133   int ret = init();
00134   if(ret)
00135   {
00136     return ret;
00137   }
00138 
00139   if (m_onePort)
00140   {
00141      m_smsInit = false; //SMS status reset
00142      m_ussdInit = false; //USSD status reset
00143      m_linkMonitorInit = false; //Link monitor status reset
00144   }
00145 
00146   ATCommandsInterface::ATResult result;
00147 
00148   if(apn != NULL)
00149   {
00150     char cmd[48];
00151     int tries = 30;
00152     sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
00153     do //Try 30 times because for some reasons it can fail *a lot* with the K3772-Z dongle
00154     {
00155       ret = m_at.executeSimple(cmd, &result);
00156       DBG("Result of command: Err code=%d", ret);
00157       if(ret)
00158       {
00159         Thread::wait(500);
00160       }
00161     } while(ret && --tries);
00162     DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
00163     DBG("APN set to %s", apn);
00164   }
00165 
00166   //Connect
00167   DBG("Connecting");
00168   if (m_onePort)
00169   {
00170     m_at.close(); // Closing AT parser
00171     m_atOpen = false; //Will need to be reinitialized afterwards
00172   }
00173   
00174   DBG("Connecting PPP");
00175 
00176   ret = m_ppp.connect();
00177   DBG("Result of connect: Err code=%d", ret);
00178   return ret;
00179 }
00180 
00181 
00182 int UbloxModem::disconnect()
00183 {
00184   DBG("Disconnecting from PPP");
00185   int ret = m_ppp.disconnect();
00186   if(ret)
00187   {
00188     ERR("Disconnect returned %d, still trying to disconnect", ret);
00189   }
00190 
00191   //Ugly but leave dongle time to recover
00192   Thread::wait(500);
00193 
00194   if (m_onePort)
00195   {
00196     //ATCommandsInterface::ATResult result;
00197     DBG("Starting AT thread");
00198     ret = m_at.open();
00199     if(ret)
00200     {
00201       return ret;
00202     }
00203   }
00204 
00205   DBG("Trying to hangup");
00206 
00207   if (m_onePort)
00208   {
00209     //Reinit AT parser
00210     ret = m_at.init(false);
00211     DBG("Result of command: Err code=%d\n", ret);
00212     if(ret)
00213     {
00214       m_at.close(); // Closing AT parser
00215       DBG("AT Parser closed, could not complete disconnection");
00216       return NET_TIMEOUT;
00217     }
00218   
00219   }
00220   return OK;
00221 }
00222 
00223 int UbloxModem::sendSM(const char* number, const char* message)
00224 {
00225   int ret = init();
00226   if(ret)
00227   {
00228     return ret;
00229   }
00230 
00231   ISMSInterface* sms;
00232   if (m_type == LISA_C200)  sms = &m_CdmaSms;
00233   else                      sms = &m_GsmSms;
00234   if(!m_smsInit)
00235   {
00236     ret = sms->init();
00237     if(ret)
00238     {
00239       return ret;
00240     }
00241     m_smsInit = true;
00242   }
00243 
00244   ret = sms->send(number, message);
00245   if(ret)
00246   {
00247     return ret;
00248   }
00249 
00250   return OK;
00251 }
00252 
00253 int UbloxModem::getSM(char* number, char* message, size_t maxLength)
00254 {
00255   int ret = init();
00256   if(ret)
00257   {
00258     return ret;
00259   }
00260 
00261   ISMSInterface* sms;
00262   if (m_type == LISA_C200)  sms = &m_CdmaSms;
00263   else                      sms = &m_GsmSms;
00264   if(!m_smsInit)
00265   {
00266     ret = sms->init();
00267     if(ret)
00268     {
00269       return ret;
00270     }
00271     m_smsInit = true;
00272   }
00273 
00274   ret = sms->get(number, message, maxLength);
00275   if(ret)
00276   {
00277     return ret;
00278   }
00279 
00280   return OK;
00281 }
00282 
00283 int UbloxModem::getSMCount(size_t* pCount)
00284 {
00285   int ret = init();
00286   if(ret)
00287   {
00288     return ret;
00289   }
00290 
00291   ISMSInterface* sms;
00292   if (m_type == LISA_C200)  sms = &m_CdmaSms;
00293   else                      sms = &m_GsmSms;
00294   if(!m_smsInit)
00295   {
00296     ret = sms->init();
00297     if(ret)
00298     {
00299       return ret;
00300     }
00301     m_smsInit = true;
00302   }
00303 
00304   ret = sms->getCount(pCount);
00305   if(ret)
00306   {
00307     return ret;
00308   }
00309 
00310   return OK;
00311 }
00312 
00313 ATCommandsInterface* UbloxModem::getATCommandsInterface()
00314 {
00315   return &m_at;
00316 }
00317 
00318 int UbloxModem::init()
00319 {
00320   if(m_atOpen)
00321   {
00322     return OK;
00323   }
00324   
00325   DBG("Starting AT thread if needed");
00326   int ret = m_at.open();
00327   if(ret)
00328   {
00329     return ret;
00330   }
00331   
00332   DBG("Sending initialisation commands");
00333   ret = m_at.init(false);
00334   if(ret)
00335   {
00336     return ret;
00337   }
00338   
00339   
00340   ATCommandsInterface::ATResult result;
00341   genericAtProcessor atiProcessor;
00342   ret = m_at.execute("ATI", &atiProcessor, &result);
00343   if (OK != ret)
00344     return ret;
00345   const char* info = atiProcessor.getResponse();
00346   INFO("Modem Identification [%s]", info);
00347   if (strstr(info, "LISA-C200")) {
00348       m_type = LISA_C200;
00349       m_onePort = true; // force use of only one port
00350   }
00351   else if (strstr(info, "LISA-U200")) {
00352       m_type = LISA_U200;
00353   }
00354   else if (strstr(info, "SARA-G350")) {
00355       m_type = SARA_G350;
00356   }
00357   
00358   // enable the network indicator 
00359   if (m_type == SARA_G350) {
00360       m_at.executeSimple("AT+UGPIOC=16,2", &result);
00361   }
00362   else if (m_type == LISA_U200) {
00363       m_at.executeSimple("AT+UGPIOC=20,2", &result); 
00364   }
00365   else if (m_type == LISA_C200) {
00366       // LISA-C200 02S/22S : GPIO1 do not support network status indication
00367       // m_at.executeSimple("AT+UGPIOC=20,2", &result); 
00368   }
00369   INFO("Modem Identification [%s]", info);
00370   
00371   CREGProcessor cregProcessor(m_type != LISA_C200);
00372   //Wait for network registration
00373   do
00374   {
00375     DBG("Waiting for network registration");
00376     ret = m_at.execute(cregProcessor.getAtCommand(), &cregProcessor, &result);
00377     DBG("Result of command: Err code=%d\n", ret);
00378     DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
00379     if(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING)
00380     {
00381       Thread::wait(3000);
00382     }
00383   } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING);
00384   if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED)
00385   {
00386     ERR("Registration denied");
00387     return NET_AUTH;
00388   }
00389  
00390   m_atOpen = true;
00391 
00392   return OK;
00393 }
00394 
00395 int UbloxModem::cleanup()
00396 {
00397   if(m_ppp.isConnected())
00398   {
00399     WARN("Data connection is still open"); //Try to encourage good behaviour from the user
00400     m_ppp.disconnect(); 
00401   }
00402   
00403   m_smsInit = false;
00404   m_ussdInit = false;
00405   m_linkMonitorInit = false;
00406   //We don't reset m_ipInit as PPPIPInterface::init() only needs to be called once
00407   
00408   if(m_atOpen)
00409   {
00410     m_at.close();
00411     m_atOpen = false;
00412   }
00413   
00414   return OK;
00415 }
00416 
00417 int UbloxModem::sendUSSD(const char* command, char* result, size_t maxLength)
00418 {
00419   int ret = init();
00420   if(ret)
00421   {
00422     return ret;
00423   }
00424 
00425   if(!m_ussdInit)
00426   {
00427     ret = m_ussd.init();
00428     if(ret)
00429     {
00430       return ret;
00431     }
00432     m_ussdInit = true;
00433   }
00434 
00435   ret = m_ussd.send(command, result, maxLength);
00436   if(ret)
00437   {
00438     return ret;
00439   }
00440 
00441   return OK;
00442 }
00443 
00444 int UbloxModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer)
00445 {
00446   int ret = init();
00447   if(ret)
00448   {
00449     return ret;
00450   }
00451   
00452   if(!m_linkMonitorInit)
00453   {
00454     ret = m_linkMonitor.init(m_type != LISA_C200);
00455     if(ret)
00456     {
00457       return ret;
00458     }
00459     m_linkMonitorInit = true;
00460   }
00461 
00462   ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer);
00463   if(ret)
00464   {
00465     return ret;
00466   }
00467 
00468   return OK;
00469 }
00470 
00471 int UbloxModem::getPhoneNumber(char* phoneNumber)
00472 {
00473   int ret = init();
00474   if(ret)
00475   {
00476     return ret;
00477   }
00478   
00479   if(!m_linkMonitorInit)
00480   {
00481     ret = m_linkMonitor.init(m_type != LISA_C200);
00482     if(ret)
00483     {
00484       return ret;
00485     }
00486     m_linkMonitorInit = true;
00487   }
00488 
00489   ret = m_linkMonitor.getPhoneNumber(phoneNumber);
00490   if(ret)
00491   {
00492     return ret;
00493   }
00494 
00495   return OK;
00496 }
00497 
00498 #include "USBHost.h"
00499 #include "UbloxGSMModemInitializer.h"
00500 #include "UbloxCDMAModemInitializer.h"
00501 
00502 UbloxUSBModem::UbloxUSBModem() :
00503    UbloxModem(&m_atStream, &m_pppStream),
00504    m_dongle(),                              // Construct WANDongle: USB interface with two serial channels to the modem (USBSerialStream objects)
00505    m_atStream(m_dongle.getSerial(1)),       // AT commands are sent down one serial channel.
00506    m_pppStream(m_dongle.getSerial(0)),      // PPP connections are managed via another serial channel.
00507    m_dongleConnected(false)                 // Dongle is initially not ready for anything
00508 {
00509     USBHost* host = USBHost::getHostInst();
00510     m_dongle.addInitializer(new UbloxGSMModemInitializer(host));
00511     m_dongle.addInitializer(new UbloxCDMAModemInitializer(host));
00512 }
00513 
00514 int UbloxUSBModem::init()
00515 {
00516   if( !m_dongleConnected )
00517   {
00518     m_dongleConnected = true;
00519     while( !m_dongle.connected() )
00520     {
00521       m_dongle.tryConnect();
00522       Thread::wait(10);
00523     }
00524     if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAU200)
00525     {
00526       INFO("Using a u-blox LISA-U200 3G/WCDMA Modem");
00527       m_type = LISA_U200;
00528     }
00529     else if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAC200)
00530     {
00531       INFO("Using a u-blox LISA-C200 CDMA Modem");
00532       m_type = LISA_C200;
00533       m_onePort = true;
00534     }
00535     else
00536     {
00537       WARN("Using an Unknown Dongle");
00538     }
00539   }
00540   return UbloxModem::init();
00541 }
00542 
00543 int UbloxUSBModem::cleanup()
00544 {
00545   UbloxModem::cleanup();
00546   m_dongle.disconnect();
00547   m_dongleConnected = false;
00548   return OK;
00549 }
00550 
00551 UbloxSerModem::UbloxSerModem() :
00552    UbloxModem(&m_atStream, NULL),
00553    m_Serial(P0_15/*MDMTXD*/,P0_16/*MDMRXD*/),
00554    m_atStream(m_Serial)
00555 {
00556   m_Serial.baud(115200/*MDMBAUD*/);
00557   m_Serial.set_flow_control(SerialBase::RTSCTS, P0_22/*MDMRTS*/, P0_17/*MDMCTS*/);
00558 }
00559