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 UbloxUSBGSMModem.cpp Source File

UbloxUSBGSMModem.cpp

00001 /* UbloxUSBGSMModem.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__ "UbloxUSBGSMModem.cpp"
00023 #endif
00024 
00025 #include "core/fwk.h"
00026 
00027 #include "UbloxUSBGSMModem.h"
00028 #include "UbloxGSMModemInitializer.h"
00029 #include "USBHost.h"
00030 
00031 UbloxUSBGSMModem::UbloxUSBGSMModem(PinName powerGatingPin /*= NC*/, bool powerGatingOnWhenPinHigh /* = true*/) :
00032    m_dongle(),                              // Construct WANDongle: USB interface with two serial channels to the modem (USBSerialStream objects)
00033    m_atStream(m_dongle.getSerial(1)),       // AT commands are sent down one serial channel.
00034    m_pppStream(m_dongle.getSerial(0)),      // PPP connections are managed via another serial channel.
00035    m_at(&m_atStream),                       // Construct ATCommandsInterface with the AT serial channel
00036    m_sms(&m_at),                            // Construct SMSInterface with the ATCommandsInterface
00037    m_ussd(&m_at),                           // Construct USSDInterface with the ATCommandsInterface
00038    m_linkMonitor(&m_at),                    // Construct LinkMonitor with the ATCommandsInterface
00039    m_ppp(&m_pppStream),                     // Construct PPPIPInterface with the PPP serial channel
00040    m_dongleConnected(false),                // Dongle is initially not ready for anything
00041    m_ipInit(false),                         // PPIPInterface connection is initially down
00042    m_smsInit(false),                        // SMSInterface starts un-initialised
00043    m_ussdInit(false),                       // USSDInterface starts un-initialised 
00044    m_linkMonitorInit(false),                // LinkMonitor subsystem starts un-initialised
00045    m_atOpen(false),                         // ATCommandsInterface starts in a closed state
00046    m_powerGatingPin(powerGatingPin),        // set power gating pin
00047    m_powerGatingOnWhenPinHigh(powerGatingOnWhenPinHigh) // set state semantics for power gating pin
00048 {
00049   USBHost* host = USBHost::getHostInst();
00050   m_dongle.addInitializer(new UbloxGSMModemInitializer(host));
00051   if( m_powerGatingPin != NC )
00052   {
00053     power(false); //Dongle will have to be powered on manually
00054   }
00055 }
00056 
00057 class CREGProcessor : public IATCommandsProcessor
00058 {
00059 public:
00060   CREGProcessor() : status(STATUS_REGISTERING)
00061   {
00062 
00063   }
00064   enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
00065   REGISTERING_STATUS getStatus()
00066   {
00067     return status;
00068   }
00069 private:
00070   virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
00071   {
00072     int r;
00073     if( sscanf(line, "+CREG: %*d,%d", &r) == 1 )
00074     {
00075       switch(r)
00076       {
00077       case 1:
00078       case 5:
00079         status = STATUS_OK;
00080         break;
00081       case 0:
00082       case 2:
00083         status = STATUS_REGISTERING;
00084         break;
00085       case 3:
00086       default:
00087         status = STATUS_FAILED;
00088         break;
00089       }
00090     }
00091     return OK;
00092   }
00093   virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
00094   {
00095     return OK;
00096   }
00097   volatile REGISTERING_STATUS status;
00098 };
00099 
00100 #if 0
00101 class COPSProcessor : public IATCommandsProcessor
00102 {
00103 public:
00104   COPSProcessor() : valid(false)
00105   {
00106     network[0] = '\0';
00107     apn[0] = '\0';
00108     bearer[0] = '\0';
00109   }
00110   char* getNetwork()
00111   {
00112     return network;
00113   }
00114   char* getAPN()
00115   {
00116     return apn;
00117   }
00118   char* getBearer()
00119   {
00120     return bearer;
00121   }
00122   bool isValid()
00123   {
00124     return valid;
00125   }
00126 private:
00127   virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
00128   {
00129     int networkId;
00130     int bearerId;
00131     int s = sscanf(line, "+COPS: %*d,%*d,\"%d\",%d", &networkId, &bearerId);
00132     if( s == 2 )
00133     {
00134       switch(networkId)
00135       {
00136       case 23415:
00137         strcpy(network, "Vodafone UK");
00138         strcpy(apn, "pp.vodafone.co.uk");
00139         valid = true;
00140         break;
00141       case 20810:
00142         strcpy(network, "SFR FR");
00143         strcpy(apn, "websfr");
00144         valid = true;
00145         break;
00146       default:
00147         break;
00148       }
00149     }
00150     else
00151     {
00152       return OK;
00153     }
00154     switch(bearerId)
00155     {
00156     case 0: strcpy(bearer, "GSM"); break;
00157     case 1: strcpy(bearer, "GSM Compact"); break;
00158     case 2: strcpy(bearer, "UTRAN"); break;
00159     case 3: strcpy(bearer, "GSM w/EGPRS"); break;
00160     case 4: strcpy(bearer, "UTRAN w/HSDPA"); break;
00161     case 5: strcpy(bearer, "UTRAN w/HSUPA"); break;
00162     case 6: strcpy(bearer, "UTRAN w/HSDPA and HSUPA"); break;
00163     case 7: strcpy(bearer, "E-UTRAN"); break;
00164 
00165     default:
00166       break;
00167     }
00168     return OK;
00169   }
00170   virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
00171   {
00172     return OK;
00173   }
00174   char network[24];
00175   char bearer[24];
00176   char apn[24];
00177   volatile bool valid;
00178 };
00179 #endif
00180 
00181 int UbloxUSBGSMModem::connect(const char* apn, const char* user, const char* password)
00182 {
00183   if( !m_ipInit )
00184   {
00185     m_ipInit = true;
00186     m_ppp.init();
00187   }
00188   m_ppp.setup(user, password, DEFAULT_MSISDN_GSM);
00189 
00190   int ret = init();
00191   if(ret)
00192   {
00193     return ret;
00194   }
00195 
00196   #if USE_ONE_PORT
00197   m_smsInit = false; //SMS status reset
00198   m_ussdInit = false; //USSD status reset
00199   m_linkMonitorInit = false; //Link monitor status reset
00200   #endif
00201 
00202   ATCommandsInterface::ATResult result;
00203 
00204   #if 0
00205   //Get network info & select corresponding APN
00206   COPSProcessor copsProcessor;
00207   DBG("Get network info & select APN from DB");
00208   ret = m_at.execute("AT+COPS=,2;+COPS?", &copsProcessor, &result); //Configure to get operator's info in numeric code & get operator's id
00209   DBG("Result of command: Err code=%d", ret);
00210   DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
00211 
00212   if(!copsProcessor.isValid())
00213   {
00214     WARN("Connected to an unknown network, try to connect with default parameters");
00215     DBG("Connected with %s", copsProcessor.getBearer());
00216   }
00217   else
00218   {
00219     DBG("Connected to %s with %s", copsProcessor.getNetwork(), copsProcessor.getBearer());
00220     char cmd[48];
00221     int tries = 3;
00222     sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", copsProcessor.getAPN());
00223     do //Try 3 times because for some reasons it can fail with the K3772-Z dongle
00224     {
00225       ret = m_at.executeSimple(cmd, &result);
00226       DBG("Result of command: Err code=%d", ret);
00227     } while(ret && --tries);
00228     DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
00229     DBG("APN set to %s", copsProcessor.getAPN());
00230   }
00231   #else
00232   if(apn != NULL)
00233   {
00234     char cmd[48];
00235     int tries = 30;
00236     sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
00237     do //Try 30 times because for some reasons it can fail *a lot* with the K3772-Z dongle
00238     {
00239       ret = m_at.executeSimple(cmd, &result);
00240       DBG("Result of command: Err code=%d", ret);
00241       if(ret)
00242       {
00243         Thread::wait(500);
00244       }
00245     } while(ret && --tries);
00246     DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
00247     DBG("APN set to %s", apn);
00248   }
00249   #endif
00250 
00251 
00252   //Connect
00253   DBG("Connecting");
00254   #if 0
00255   ret = m_at.executeSimple("ATDT *99#", &result);
00256   DBG("Result of command: Err code=%d", ret);
00257   DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
00258   #endif
00259   #if USE_ONE_PORT
00260   m_at.close(); // Closing AT parser
00261   m_atOpen = false; //Will need to be reinitialized afterwards
00262   #endif
00263 
00264   #if 0
00265   DBG("AT Parser closed");
00266   if( (ret!=NET_MOREINFO) || (result.result != ATCommandsInterface::ATResult::AT_CONNECT))
00267   {
00268     ERR("Could not connect");
00269     return ret; //Could not connect
00270   }
00271   #endif
00272   DBG("Connecting PPP");
00273 
00274   ret = m_ppp.connect();
00275   DBG("Result of connect: Err code=%d", ret);
00276   return ret;
00277 }
00278 
00279 
00280 int UbloxUSBGSMModem::disconnect()
00281 {
00282   DBG("Disconnecting from PPP");
00283   int ret = m_ppp.disconnect();
00284   if(ret)
00285   {
00286     ERR("Disconnect returned %d, still trying to disconnect", ret);
00287   }
00288 
00289   //Ugly but leave dongle time to recover
00290   Thread::wait(500);
00291 
00292   #if USE_ONE_PORT
00293   ATCommandsInterface::ATResult result;
00294   DBG("Starting AT thread");
00295   ret = m_at.open();
00296   if(ret)
00297   {
00298     return ret;
00299   }
00300   #endif
00301 
00302   DBG("Trying to hangup");
00303 
00304   #if 0 //Does not appear to work
00305   int tries = 10;
00306   do
00307   {
00308     ret = m_at.executeSimple("+++", &result, 1000);
00309     DBG("Result of command: Err code=%d\n", ret);
00310     DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
00311   } while(tries-- && ret);
00312   if(!ret)
00313   {
00314     ret = m_at.executeSimple("ATH", &result);
00315     DBG("Result of command: Err code=%d\n", ret);
00316     DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
00317   }
00318   #endif
00319 
00320   #if USE_ONE_PORT
00321   //Reinit AT parser
00322   ret = m_at.init();
00323   DBG("Result of command: Err code=%d\n", ret);
00324   if(ret)
00325   {
00326     m_at.close(); // Closing AT parser
00327     DBG("AT Parser closed, could not complete disconnection");
00328     return NET_TIMEOUT;
00329   }
00330 
00331   #if 0
00332   m_at.close(); // Closing AT parser
00333   DBG("AT Parser closed");
00334   #endif
00335   #endif
00336   return OK;
00337 }
00338 
00339 int UbloxUSBGSMModem::sendSM(const char* number, const char* message)
00340 {
00341   int ret = init();
00342   if(ret)
00343   {
00344     return ret;
00345   }
00346 
00347   if(!m_smsInit)
00348   {
00349     ret = m_sms.init();
00350     if(ret)
00351     {
00352       return ret;
00353     }
00354     m_smsInit = true;
00355   }
00356 
00357   ret = m_sms.send(number, message);
00358   if(ret)
00359   {
00360     return ret;
00361   }
00362 
00363   return OK;
00364 }
00365 
00366 int UbloxUSBGSMModem::getSM(char* number, char* message, size_t maxLength)
00367 {
00368   int ret = init();
00369   if(ret)
00370   {
00371     return ret;
00372   }
00373 
00374   if(!m_smsInit)
00375   {
00376     ret = m_sms.init();
00377     if(ret)
00378     {
00379       return ret;
00380     }
00381     m_smsInit = true;
00382   }
00383 
00384   ret = m_sms.get(number, message, maxLength);
00385   if(ret)
00386   {
00387     return ret;
00388   }
00389 
00390   return OK;
00391 }
00392 
00393 int UbloxUSBGSMModem::getSMCount(size_t* pCount)
00394 {
00395   int ret = init();
00396   if(ret)
00397   {
00398     return ret;
00399   }
00400 
00401   if(!m_smsInit)
00402   {
00403     ret = m_sms.init();
00404     if(ret)
00405     {
00406       return ret;
00407     }
00408     m_smsInit = true;
00409   }
00410 
00411   ret = m_sms.getCount(pCount);
00412   if(ret)
00413   {
00414     return ret;
00415   }
00416 
00417   return OK;
00418 }
00419 
00420 int UbloxUSBGSMModem::sendUSSD(const char* command, char* result, size_t maxLength)
00421 {
00422   int ret = init();
00423   if(ret)
00424   {
00425     return ret;
00426   }
00427 
00428   if(!m_ussdInit)
00429   {
00430     ret = m_ussd.init();
00431     if(ret)
00432     {
00433       return ret;
00434     }
00435     m_ussdInit = true;
00436   }
00437 
00438   ret = m_ussd.send(command, result, maxLength);
00439   if(ret)
00440   {
00441     return ret;
00442   }
00443 
00444   return OK;
00445 }
00446 
00447 int UbloxUSBGSMModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer)
00448 {
00449   int ret = init();
00450   if(ret)
00451   {
00452     return ret;
00453   }
00454   
00455   if(!m_linkMonitorInit)
00456   {
00457     ret = m_linkMonitor.init();
00458     if(ret)
00459     {
00460       return ret;
00461     }
00462     m_linkMonitorInit = true;
00463   }
00464 
00465   ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer);
00466   if(ret)
00467   {
00468     return ret;
00469   }
00470 
00471   return OK;
00472 }
00473 
00474 
00475 ATCommandsInterface* UbloxUSBGSMModem::getATCommandsInterface()
00476 {
00477   return &m_at;
00478 }
00479 
00480 int UbloxUSBGSMModem::power(bool enable)
00481 {
00482   if( m_powerGatingPin == NC )
00483   {
00484     return NET_INVALID; //A pin name has not been provided in the constructor
00485   }
00486 
00487   if(!enable) //Will force components to re-init
00488   {
00489     cleanup();
00490   }
00491   
00492   DigitalOut powerGatingOut(m_powerGatingPin);
00493   powerGatingOut = m_powerGatingOnWhenPinHigh?enable:!enable;
00494 
00495   return OK;
00496 }
00497 
00498 bool UbloxUSBGSMModem::power()
00499 {
00500   if( m_powerGatingPin == NC )
00501   {
00502     return true; //Assume power is always on 
00503   }
00504   
00505   DigitalOut powerGatingOut(m_powerGatingPin);
00506   return m_powerGatingOnWhenPinHigh?powerGatingOut:!powerGatingOut;
00507 }
00508 
00509 int UbloxUSBGSMModem::init()
00510 {
00511   if( !m_dongleConnected )
00512   {
00513     if(!power())
00514     {
00515       //Obviously cannot initialize the dongle if it is disconnected...
00516       ERR("Power is off");
00517       return NET_INVALID;
00518     }
00519     m_dongleConnected = true;
00520     while( !m_dongle.connected() )
00521     {
00522       m_dongle.tryConnect();
00523       Thread::wait(10);
00524     }
00525   }
00526 
00527   if(m_atOpen)
00528   {
00529     return OK;
00530   }
00531   
00532   DBG("Starting AT thread if needed");
00533   int ret = m_at.open();
00534   if(ret)
00535   {
00536     return ret;
00537   }
00538   
00539   DBG("Sending initialisation commands");
00540   ret = m_at.init();
00541   if(ret)
00542   {
00543     return ret;
00544   }
00545 
00546   if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAU200)
00547   {
00548     INFO("Using a u-blox LISA-U");
00549   }
00550   else
00551   {
00552     WARN("Using an Unknown Dongle");
00553   }
00554 
00555   ATCommandsInterface::ATResult result;
00556 
00557   //Wait for network registration
00558   CREGProcessor cregProcessor;
00559   do
00560   {
00561     DBG("Waiting for network registration");
00562     ret = m_at.execute("AT+CREG?", &cregProcessor, &result);
00563     DBG("Result of command: Err code=%d\n", ret);
00564     DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
00565     if(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING)
00566     {
00567       Thread::wait(3000);
00568     }
00569   } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING);
00570   if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED)
00571   {
00572     ERR("Registration denied");
00573     return NET_AUTH;
00574   }
00575 
00576   m_atOpen = true;
00577 
00578   return OK;
00579 }
00580 
00581 int UbloxUSBGSMModem::cleanup()
00582 {
00583   if(m_ppp.isConnected())
00584   {
00585     WARN("Data connection is still open"); //Try to encourage good behaviour from the user
00586     m_ppp.disconnect(); 
00587   }
00588   
00589   m_smsInit = false;
00590   m_ussdInit = false;
00591   m_linkMonitorInit = false;
00592   //We don't reset m_ipInit as PPPIPInterface::init() only needs to be called once
00593   
00594   if(m_atOpen)
00595   {
00596     m_at.close();
00597     m_atOpen = false;
00598   }
00599   
00600   m_dongle.disconnect();
00601   m_dongleConnected = false;
00602   
00603   return OK;
00604 }
00605