Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
Generated on Tue Jul 12 2022 17:34:58 by
1.7.2