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.
UbloxCellularBase.cpp
00001 /* Copyright (c) 2017 ublox Limited 00002 * 00003 * Licensed under the Apache License, Version 2.0 (the "License"); 00004 * you may not use this file except in compliance with the License. 00005 * You may obtain a copy of the License at 00006 * 00007 * http://www.apache.org/licenses/LICENSE-2.0 00008 * 00009 * Unless required by applicable law or agreed to in writing, software 00010 * distributed under the License is distributed on an "AS IS" BASIS, 00011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00012 * See the License for the specific language governing permissions and 00013 * limitations under the License. 00014 */ 00015 00016 #include "UARTSerial.h" 00017 #include "APN_db.h" 00018 #include "UbloxCellularBase.h" 00019 #include "onboard_modem_api.h" 00020 #ifdef FEATURE_COMMON_PAL 00021 #include "mbed_trace.h" 00022 #define TRACE_GROUP "UCB" 00023 #else 00024 #define tr_debug(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__) 00025 #define tr_info(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__) 00026 #define tr_warn(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__) 00027 #define tr_error(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__) 00028 #endif 00029 00030 /* Array to convert the 3G qual number into a median EC_NO_LEV number. 00031 */ 00032 /* 0 1 2 3 4 5 6 7 */ 00033 /* 44, 41, 35, 29, 23, 17, 11, 7*/ 00034 const int qualConvert3G[] = {-2, -4, -7, -10, -13, -16, -19, -21}; 00035 00036 /* Array to convert the 3G "rssi" number into a dBm RSCP value rounded up to the 00037 * nearest whole number. 00038 */ 00039 const int rscpConvert3G[] = {-108, -105, -103, -100, -98, -96, -94, -93, /* 0 - 7 */ 00040 -91, -89, -88, -85, -83, -80, -78, -76, /* 8 - 15 */ 00041 -74, -73, -70, -68, -66, -64, -63, -60, /* 16 - 23 */ 00042 -58, -56, -54, -53, -51, -49, -48, -46}; /* 24 - 31 */ 00043 00044 /* Array to convert the LTE rssi number into a dBm value rounded up to the 00045 * nearest whole number. 00046 */ 00047 const int rssiConvertLte[] = {-118, -115, -113, -110, -108, -105, -103, -100, /* 0 - 7 */ 00048 -98, -95, -93, -90, -88, -85, -83, -80, /* 8 - 15 */ 00049 -78, -76, -74, -73, -71, -69, -68, -65, /* 16 - 23 */ 00050 -63, -61, -60, -59, -58, -55, -53, -48}; /* 24 - 31 */ 00051 00052 /********************************************************************** 00053 * PRIVATE METHODS 00054 **********************************************************************/ 00055 00056 void UbloxCellularBase::set_nwk_reg_status_csd(int status) 00057 { 00058 switch (status) { 00059 case CSD_NOT_REGISTERED_NOT_SEARCHING: 00060 case CSD_NOT_REGISTERED_SEARCHING: 00061 tr_info("Not (yet) registered for circuit switched service"); 00062 break; 00063 case CSD_REGISTERED: 00064 case CSD_REGISTERED_ROAMING: 00065 tr_info("Registered for circuit switched service"); 00066 break; 00067 case CSD_REGISTRATION_DENIED: 00068 tr_info("Circuit switched service denied"); 00069 break; 00070 case CSD_UNKNOWN_COVERAGE: 00071 tr_info("Out of circuit switched service coverage"); 00072 break; 00073 case CSD_SMS_ONLY: 00074 tr_info("SMS service only"); 00075 break; 00076 case CSD_SMS_ONLY_ROAMING: 00077 tr_info("SMS service only"); 00078 break; 00079 case CSD_CSFB_NOT_PREFERRED: 00080 tr_info("Registered for circuit switched service with CSFB not preferred"); 00081 break; 00082 default: 00083 tr_info("Unknown circuit switched service registration status. %d", status); 00084 break; 00085 } 00086 00087 _dev_info.reg_status_csd = static_cast<NetworkRegistrationStatusCsd>(status); 00088 } 00089 00090 void UbloxCellularBase::set_nwk_reg_status_psd(int status) 00091 { 00092 switch (status) { 00093 case PSD_NOT_REGISTERED_NOT_SEARCHING: 00094 case PSD_NOT_REGISTERED_SEARCHING: 00095 tr_info("Not (yet) registered for packet switched service"); 00096 break; 00097 case PSD_REGISTERED: 00098 case PSD_REGISTERED_ROAMING: 00099 tr_info("Registered for packet switched service"); 00100 break; 00101 case PSD_REGISTRATION_DENIED: 00102 tr_info("Packet switched service denied"); 00103 break; 00104 case PSD_UNKNOWN_COVERAGE: 00105 tr_info("Out of packet switched service coverage"); 00106 break; 00107 case PSD_EMERGENCY_SERVICES_ONLY: 00108 tr_info("Limited access for packet switched service. Emergency use only."); 00109 break; 00110 default: 00111 tr_info("Unknown packet switched service registration status. %d", status); 00112 break; 00113 } 00114 00115 _dev_info.reg_status_psd = static_cast<NetworkRegistrationStatusPsd>(status); 00116 } 00117 00118 void UbloxCellularBase::set_nwk_reg_status_eps(int status) 00119 { 00120 switch (status) { 00121 case EPS_NOT_REGISTERED_NOT_SEARCHING: 00122 case EPS_NOT_REGISTERED_SEARCHING: 00123 tr_info("Not (yet) registered for EPS service"); 00124 break; 00125 case EPS_REGISTERED: 00126 case EPS_REGISTERED_ROAMING: 00127 tr_info("Registered for EPS service"); 00128 break; 00129 case EPS_REGISTRATION_DENIED: 00130 tr_info("EPS service denied"); 00131 break; 00132 case EPS_UNKNOWN_COVERAGE: 00133 tr_info("Out of EPS service coverage"); 00134 break; 00135 case EPS_EMERGENCY_SERVICES_ONLY: 00136 tr_info("Limited access for EPS service. Emergency use only."); 00137 break; 00138 default: 00139 tr_info("Unknown EPS service registration status. %d", status); 00140 break; 00141 } 00142 00143 _dev_info.reg_status_eps = static_cast<NetworkRegistrationStatusEps>(status); 00144 } 00145 00146 void UbloxCellularBase::set_rat(int acTStatus) 00147 { 00148 switch (acTStatus) { 00149 case GSM: 00150 case COMPACT_GSM: 00151 tr_info("Connected in GSM"); 00152 break; 00153 case UTRAN: 00154 tr_info("Connected to UTRAN"); 00155 break; 00156 case EDGE: 00157 tr_info("Connected to EDGE"); 00158 break; 00159 case HSDPA: 00160 tr_info("Connected to HSDPA"); 00161 break; 00162 case HSUPA: 00163 tr_info("Connected to HSPA"); 00164 break; 00165 case HSDPA_HSUPA: 00166 tr_info("Connected to HDPA/HSPA"); 00167 break; 00168 case LTE: 00169 tr_info("Connected to LTE"); 00170 break; 00171 case EC_GSM_IoT: 00172 tr_info("Connected to EC_GSM_IoT"); 00173 break; 00174 case E_UTRAN_NB_S1: 00175 tr_info("Connected to E_UTRAN NB1"); 00176 break; 00177 default: 00178 tr_info("Unknown RAT %d", acTStatus); 00179 break; 00180 } 00181 00182 _dev_info.rat = static_cast<RadioAccessNetworkType>(acTStatus); 00183 } 00184 00185 bool UbloxCellularBase::get_iccid() 00186 { 00187 bool success; 00188 LOCK(); 00189 00190 MBED_ASSERT(_at != NULL); 00191 00192 // Returns the ICCID (Integrated Circuit Card ID) of the SIM-card. 00193 // ICCID is a serial number identifying the SIM. 00194 // AT Command Manual UBX-13002752, section 4.12 00195 success = _at->send("AT+CCID") && _at->recv("+CCID: %20[^\n]\nOK\n", _dev_info.iccid); 00196 tr_info("DevInfo: ICCID=%s", _dev_info.iccid); 00197 00198 UNLOCK(); 00199 return success; 00200 } 00201 00202 bool UbloxCellularBase::get_imsi() 00203 { 00204 bool success; 00205 LOCK(); 00206 00207 MBED_ASSERT(_at != NULL); 00208 00209 // International mobile subscriber identification 00210 // AT Command Manual UBX-13002752, section 4.11 00211 success = _at->send("AT+CIMI") && _at->recv("%15[^\n]\nOK\n", _dev_info.imsi); 00212 tr_info("DevInfo: IMSI=%s", _dev_info.imsi); 00213 00214 UNLOCK(); 00215 return success; 00216 } 00217 00218 bool UbloxCellularBase::get_imei() 00219 { 00220 bool success; 00221 LOCK(); 00222 00223 MBED_ASSERT(_at != NULL); 00224 00225 // International mobile equipment identifier 00226 // AT Command Manual UBX-13002752, section 4.7 00227 success = _at->send("AT+CGSN") && _at->recv("%15[^\n]\nOK\n", _dev_info.imei); 00228 tr_info("DevInfo: IMEI=%s", _dev_info.imei); 00229 00230 UNLOCK(); 00231 return success; 00232 } 00233 00234 bool UbloxCellularBase::get_meid() 00235 { 00236 bool success; 00237 LOCK(); 00238 00239 MBED_ASSERT(_at != NULL); 00240 00241 // Mobile equipment identifier 00242 // AT Command Manual UBX-13002752, section 4.8 00243 success = _at->send("AT+GSN") && _at->recv("%18[^\n]\nOK\n", _dev_info.meid); 00244 tr_info("DevInfo: MEID=%s", _dev_info.meid); 00245 00246 UNLOCK(); 00247 return success; 00248 } 00249 00250 bool UbloxCellularBase::set_sms() 00251 { 00252 bool success = false; 00253 char buf[32]; 00254 LOCK(); 00255 00256 MBED_ASSERT(_at != NULL); 00257 00258 // Set up SMS format and enable URC 00259 // AT Command Manual UBX-13002752, section 11 00260 if (_at->send("AT+CMGF=1") && _at->recv("OK")) { 00261 tr_debug("SMS in text mode"); 00262 if (_at->send("AT+CNMI=2,1") && _at->recv("OK")) { 00263 tr_debug("SMS URC enabled"); 00264 // Set to CS preferred since PS preferred doesn't work 00265 // on some networks 00266 if (_at->send("AT+CGSMS=1") && _at->recv("OK")) { 00267 tr_debug("SMS set to CS preferred"); 00268 success = true; 00269 memset (buf, 0, sizeof (buf)); 00270 if (_at->send("AT+CSCA?") && 00271 _at->recv("+CSCA: \"%31[^\"]\"", buf) && 00272 _at->recv("OK")) { 00273 tr_info("SMS Service Centre address is \"%s\"", buf); 00274 } 00275 } 00276 } 00277 } 00278 00279 UNLOCK(); 00280 return success; 00281 } 00282 00283 void UbloxCellularBase::parser_abort_cb() 00284 { 00285 _at->abort(); 00286 } 00287 00288 // Callback for CME ERROR and CMS ERROR. 00289 void UbloxCellularBase::CMX_ERROR_URC() 00290 { 00291 char buf[48]; 00292 00293 if (read_at_to_char(buf, sizeof (buf), '\n') > 0) { 00294 tr_debug("AT error %s", buf); 00295 } 00296 parser_abort_cb(); 00297 } 00298 00299 // Callback for circuit switched registration URC. 00300 void UbloxCellularBase::CREG_URC() 00301 { 00302 char buf[10]; 00303 int status; 00304 int acTStatus; 00305 00306 // If this is the URC it will be a single 00307 // digit followed by \n. If it is the 00308 // answer to a CREG query, it will be 00309 // a ": %d,%d\n" where the second digit 00310 // indicates the status 00311 // Note: not calling _at->recv() from here as we're 00312 // already in an _at->recv() 00313 if (read_at_to_char(buf, sizeof (buf), '\n') > 0) { 00314 if (sscanf(buf, ": %*d,%d,%*d,%*d,%d,", &status, &acTStatus) == 2) { 00315 set_nwk_reg_status_csd(status); 00316 set_rat(acTStatus); 00317 } else if (sscanf(buf, ": %*d,%d", &status) == 1) { 00318 set_nwk_reg_status_csd(status); 00319 } else if (sscanf(buf, ": %d", &status) == 1) { 00320 set_nwk_reg_status_csd(status); 00321 } 00322 } 00323 } 00324 00325 // Callback for packet switched registration URC. 00326 void UbloxCellularBase::CGREG_URC() 00327 { 00328 char buf[10]; 00329 int status; 00330 int acTStatus; 00331 00332 // If this is the URC it will be a single 00333 // digit followed by \n. If it is the 00334 // answer to a CGREG query, it will be 00335 // a ": %d,%d\n" where the second digit 00336 // indicates the status 00337 // Note: not calling _at->recv() from here as we're 00338 // already in an _at->recv() 00339 if (read_at_to_char(buf, sizeof (buf), '\n') > 0) { 00340 if (sscanf(buf, ": %*d,%d,%*d,%*d,%d,", &status, &acTStatus) == 2) { 00341 set_nwk_reg_status_csd(status); 00342 set_rat(acTStatus); 00343 } else if (sscanf(buf, ": %*d,%d", &status) == 1) { 00344 set_nwk_reg_status_psd(status); 00345 } else if (sscanf(buf, ": %d", &status) == 1) { 00346 set_nwk_reg_status_psd(status); 00347 } 00348 } 00349 } 00350 00351 // Callback for EPS registration URC. 00352 void UbloxCellularBase::CEREG_URC() 00353 { 00354 char buf[10]; 00355 int status; 00356 int acTStatus; 00357 00358 // If this is the URC it will be a single 00359 // digit followed by \n. If it is the 00360 // answer to a CEREG query, it will be 00361 // a ": %d,%d\n" where the second digit 00362 // indicates the status 00363 // Note: not calling _at->recv() from here as we're 00364 // already in an _at->recv() 00365 if (read_at_to_char(buf, sizeof (buf), '\n') > 0) { 00366 if (sscanf(buf, ": %*d,%d,%*d,%*d,%d,", &status, &acTStatus) == 2) { 00367 set_nwk_reg_status_csd(status); 00368 set_rat(acTStatus); 00369 } else if (sscanf(buf, ": %*d,%d", &status) == 1) { 00370 set_nwk_reg_status_eps(status); 00371 } else if (sscanf(buf, ": %d", &status) == 1) { 00372 set_nwk_reg_status_eps(status); 00373 } 00374 } 00375 } 00376 00377 // Callback UMWI, just filtering it out. 00378 void UbloxCellularBase::UMWI_URC() 00379 { 00380 char buf[10]; 00381 00382 // Note: not calling _at->recv() from here as we're 00383 // already in an _at->recv() 00384 read_at_to_char(buf, sizeof (buf), '\n'); 00385 } 00386 00387 /********************************************************************** 00388 * PROTECTED METHODS 00389 **********************************************************************/ 00390 00391 #if MODEM_ON_BOARD 00392 void UbloxCellularBase::modem_init() 00393 { 00394 ::onboard_modem_init(); 00395 } 00396 00397 void UbloxCellularBase::modem_deinit() 00398 { 00399 ::onboard_modem_deinit(); 00400 } 00401 00402 void UbloxCellularBase::modem_power_up() 00403 { 00404 ::onboard_modem_power_up(); 00405 } 00406 00407 void UbloxCellularBase::modem_power_down() 00408 { 00409 ::onboard_modem_power_down(); 00410 } 00411 #else 00412 void UbloxCellularBase::modem_init() 00413 { 00414 // Meant to be overridden 00415 } 00416 00417 void UbloxCellularBase::modem_deinit() 00418 { 00419 // Meant to be overridden 00420 } 00421 00422 void UbloxCellularBase::modem_power_up() 00423 { 00424 // Meant to be overridden 00425 } 00426 00427 void UbloxCellularBase::modem_power_down() 00428 { 00429 // Mmeant to be overridden 00430 } 00431 #endif 00432 00433 // Constructor. 00434 // Note: to allow this base class to be inherited as a virtual base class 00435 // by everyone, it takes no parameters. See also comment above classInit() 00436 // in the header file. 00437 UbloxCellularBase::UbloxCellularBase() 00438 { 00439 _pin = NULL; 00440 _at = NULL; 00441 _at_timeout = AT_PARSER_TIMEOUT; 00442 _fh = NULL; 00443 _modem_initialised = false; 00444 _sim_pin_check_enabled = false; 00445 _debug_trace_on = false; 00446 memset(_plmn, 0, sizeof(_plmn)); 00447 00448 _dev_info.dev = DEV_TYPE_NONE; 00449 _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING; 00450 _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING; 00451 _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING; 00452 } 00453 00454 // Destructor. 00455 UbloxCellularBase::~UbloxCellularBase() 00456 { 00457 deinit(); 00458 delete _at; 00459 delete _fh; 00460 } 00461 00462 // Initialise the portions of this class that are parameterised. 00463 void UbloxCellularBase::baseClassInit(PinName tx, PinName rx, 00464 int baud, bool debug_on) 00465 { 00466 // Only initialise ourselves if it's not already been done 00467 if (_at == NULL) { 00468 if (_debug_trace_on == false) { 00469 _debug_trace_on = debug_on; 00470 } 00471 _baud = baud; 00472 00473 // Set up File Handle for buffered serial comms with cellular module 00474 // (which will be used by the AT parser) 00475 // Note: the UART is initialised to run no faster than 115200 because 00476 // the modems cannot reliably auto-baud at faster rates. The faster 00477 // rate is adopted later with a specific AT command and the 00478 // UARTSerial rate is adjusted at that time 00479 if (baud > 115200) { 00480 baud = 115200; 00481 } 00482 _fh = new UARTSerial(tx, rx, baud); 00483 00484 // Set up the AT parser 00485 _at = new ATCmdParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE, 00486 _at_timeout, _debug_trace_on); 00487 00488 // Error cases, out of band handling 00489 _at->oob("ERROR", callback(this, &UbloxCellularBase::parser_abort_cb)); 00490 _at->oob("+CME ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC)); 00491 _at->oob("+CMS ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC)); 00492 00493 // Registration status, out of band handling 00494 _at->oob("+CREG", callback(this, &UbloxCellularBase::CREG_URC)); 00495 _at->oob("+CGREG", callback(this, &UbloxCellularBase::CGREG_URC)); 00496 _at->oob("+CEREG", callback(this, &UbloxCellularBase::CEREG_URC)); 00497 00498 // Capture the UMWI, just to stop it getting in the way 00499 _at->oob("+UMWI", callback(this, &UbloxCellularBase::UMWI_URC)); 00500 } 00501 } 00502 00503 // Set the AT parser timeout. 00504 // Note: the AT interface should be locked before this is called. 00505 void UbloxCellularBase::at_set_timeout(int timeout) { 00506 00507 MBED_ASSERT(_at != NULL); 00508 00509 _at_timeout = timeout; 00510 _at->set_timeout(timeout); 00511 } 00512 00513 // Read up to size bytes from the AT interface up to a "end". 00514 // Note: the AT interface should be locked before this is called. 00515 int UbloxCellularBase::read_at_to_char(char * buf, int size, char end) 00516 { 00517 int count = 0; 00518 int x = 0; 00519 00520 if (size > 0) { 00521 for (count = 0; (count < size) && (x >= 0) && (x != end); count++) { 00522 x = _at->getc(); 00523 *(buf + count) = (char) x; 00524 } 00525 00526 count--; 00527 *(buf + count) = 0; 00528 00529 // Convert line endings: 00530 // If end was '\n' (0x0a) and the preceding character was 0x0d, then 00531 // overwrite that with null as well. 00532 if ((count > 0) && (end == '\n') && (*(buf + count - 1) == '\x0d')) { 00533 count--; 00534 *(buf + count) = 0; 00535 } 00536 } 00537 00538 return count; 00539 } 00540 00541 // Power up the modem. 00542 // Enables the GPIO lines to the modem and then wriggles the power line in short pulses. 00543 bool UbloxCellularBase::power_up() 00544 { 00545 bool success = false; 00546 int at_timeout; 00547 LOCK(); 00548 00549 at_timeout = _at_timeout; // Has to be inside LOCK()s 00550 00551 MBED_ASSERT(_at != NULL); 00552 00553 /* Initialize GPIO lines */ 00554 tr_info("Powering up modem..."); 00555 modem_init(); 00556 /* Give modem a little time to settle down */ 00557 wait_ms(250); 00558 00559 for (int retry_count = 0; !success && (retry_count < 20); retry_count++) { 00560 //In case of SARA-R4, modem takes a while to turn on, constantly toggling the power pin every ~2 secs causes the modem to never power up. 00561 if ( (retry_count % 5) == 0) { 00562 modem_power_up(); 00563 } 00564 wait_ms(500); 00565 // Modem tends to spit out noise during power up - don't confuse the parser 00566 _at->flush(); 00567 at_set_timeout(1000); 00568 if (_at->send("AT")) { 00569 // C027 needs a delay here 00570 wait_ms(100); 00571 if (_at->recv("OK")) { 00572 success = true; 00573 } 00574 } 00575 at_set_timeout(at_timeout); 00576 } 00577 00578 if (success) { 00579 // Set the final baud rate 00580 if (_at->send("AT+IPR=%d", _baud) && _at->recv("OK")) { 00581 // Need to wait for things to be sorted out on the modem side 00582 wait_ms(100); 00583 ((UARTSerial *)_fh)->set_baud(_baud); 00584 } 00585 00586 // Turn off modem echoing and turn on verbose responses 00587 success = _at->send("ATE0;+CMEE=2") && _at->recv("OK") && 00588 // The following commands are best sent separately 00589 _at->send("AT&K0") && _at->recv("OK") && // Turn off RTC/CTS handshaking 00590 _at->send("AT&C1") && _at->recv("OK") && // Set DCD circuit(109), changes in accordance with the carrier detect status 00591 _at->send("AT&D0") && _at->recv("OK"); // Set DTR circuit, we ignore the state change of DTR 00592 } 00593 00594 if (!success) { 00595 tr_error("Preliminary modem setup failed."); 00596 } 00597 00598 UNLOCK(); 00599 return success; 00600 } 00601 00602 // Power down modem via AT interface. 00603 void UbloxCellularBase::power_down() 00604 { 00605 LOCK(); 00606 00607 MBED_ASSERT(_at != NULL); 00608 00609 // power-off modem 00610 modem_power_down(); 00611 modem_deinit(); 00612 00613 if (_modem_initialised && (_at != NULL)) { 00614 int at_timeout = _at_timeout; // Save previous timeout 00615 _at->set_timeout(1000); 00616 // Check modem is powered off 00617 if(_at->send("AT") && _at->recv("OK")) { 00618 _at->send("AT+CPWROFF") && _at->recv("OK"); 00619 } 00620 _at->set_timeout(at_timeout); 00621 } 00622 00623 _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING; 00624 _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING; 00625 _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING; 00626 00627 UNLOCK(); 00628 } 00629 00630 // Get the device ID. 00631 bool UbloxCellularBase::set_device_identity(DeviceType *dev) 00632 { 00633 char buf[20]; 00634 bool success; 00635 LOCK(); 00636 00637 MBED_ASSERT(_at != NULL); 00638 00639 success = _at->send("ATI") && _at->recv("%19[^\n]\nOK\n", buf); 00640 00641 if (success) { 00642 if (strstr(buf, "SARA-G35")) 00643 *dev = DEV_SARA_G35; 00644 else if (strstr(buf, "LISA-U200-03S")) 00645 *dev = DEV_LISA_U2_03S; 00646 else if (strstr(buf, "LISA-U2")) 00647 *dev = DEV_LISA_U2; 00648 else if (strstr(buf, "SARA-U2")) 00649 *dev = DEV_SARA_U2; 00650 else if (strstr(buf, "SARA-R4")) 00651 *dev = DEV_SARA_R4; 00652 else if (strstr(buf, "LEON-G2")) 00653 *dev = DEV_LEON_G2; 00654 else if (strstr(buf, "TOBY-L2")) 00655 *dev = DEV_TOBY_L2; 00656 else if (strstr(buf, "MPCI-L2")) 00657 *dev = DEV_MPCI_L2; 00658 } 00659 00660 UNLOCK(); 00661 return success; 00662 } 00663 00664 // Send initialisation AT commands that are specific to the device. 00665 bool UbloxCellularBase::device_init(DeviceType dev) 00666 { 00667 bool success = false; 00668 LOCK(); 00669 00670 MBED_ASSERT(_at != NULL); 00671 00672 if ((dev == DEV_LISA_U2) || (dev == DEV_LEON_G2) || (dev == DEV_TOBY_L2)) { 00673 success = _at->send("AT+UGPIOC=20,2") && _at->recv("OK"); 00674 } else if ((dev == DEV_SARA_U2) || (dev == DEV_SARA_G35)) { 00675 success = _at->send("AT+UGPIOC=16,2") && _at->recv("OK"); 00676 } else { 00677 success = true; 00678 } 00679 00680 UNLOCK(); 00681 return success; 00682 } 00683 00684 // Get the SIM card going. 00685 bool UbloxCellularBase::initialise_sim_card() 00686 { 00687 bool success = false; 00688 int retry_count = 0; 00689 bool done = false; 00690 LOCK(); 00691 00692 MBED_ASSERT(_at != NULL); 00693 00694 /* SIM initialisation may take a significant amount, so an error is 00695 * kind of expected. We should retry 10 times until we succeed or timeout. */ 00696 for (retry_count = 0; !done && (retry_count < 10); retry_count++) { 00697 char pinstr[16]; 00698 00699 if (_at->send("AT+CPIN?") && _at->recv("+CPIN: %15[^\n]\n", pinstr) && 00700 _at->recv("OK")) { 00701 done = true; 00702 if (strcmp(pinstr, "SIM PIN") == 0) { 00703 _sim_pin_check_enabled = true; 00704 if (_at->send("AT+CPIN=\"%s\"", _pin)) { 00705 if (_at->recv("OK")) { 00706 tr_info("PIN correct"); 00707 success = true; 00708 } else { 00709 tr_error("Incorrect PIN"); 00710 } 00711 } 00712 } else if (strcmp(pinstr, "READY") == 0) { 00713 _sim_pin_check_enabled = false; 00714 tr_info("No PIN required"); 00715 success = true; 00716 } else { 00717 tr_debug("Unexpected response from SIM: \"%s\"", pinstr); 00718 } 00719 } 00720 00721 /* wait for a second before retry */ 00722 wait_ms(1000); 00723 } 00724 00725 if (done) { 00726 tr_info("SIM Ready."); 00727 } else { 00728 tr_error("SIM not ready."); 00729 } 00730 00731 UNLOCK(); 00732 return success; 00733 } 00734 00735 /********************************************************************** 00736 * PUBLIC METHODS 00737 **********************************************************************/ 00738 00739 // Initialise the modem. 00740 bool UbloxCellularBase::init(const char *pin) 00741 { 00742 int x; 00743 MBED_ASSERT(_at != NULL); 00744 00745 if (!_modem_initialised) { 00746 if (power_up()) { 00747 tr_info("Modem Ready."); 00748 if (pin != NULL) { 00749 _pin = pin; 00750 } 00751 if (initialise_sim_card()) { 00752 if (set_device_identity(&_dev_info.dev) && // Set up device identity 00753 device_init(_dev_info.dev)) {// Initialise this device 00754 // Get the integrated circuit ID of the SIM 00755 if (get_iccid()) { 00756 // Try a few times to get the IMSI (since on some modems this can 00757 // take a while to be retrieved, especially if a SIM PIN 00758 // was set) 00759 for (x = 0; (x < 3) && !get_imsi(); x++) { 00760 wait_ms(1000); 00761 } 00762 00763 if (x < 3) { // If we got the IMSI, can get the others 00764 if (get_imei() && // Get international mobile equipment identifier 00765 get_meid() && // Probably the same as the IMEI 00766 set_sms()) { // And set up SMS 00767 // The modem is initialised. 00768 _modem_initialised = true; 00769 } 00770 } 00771 } 00772 } 00773 } 00774 } 00775 } 00776 00777 return _modem_initialised; 00778 } 00779 00780 // Perform registration. 00781 bool UbloxCellularBase::nwk_registration() 00782 { 00783 bool atSuccess = false; 00784 bool registered = false; 00785 int status; 00786 int at_timeout; 00787 LOCK(); 00788 00789 at_timeout = _at_timeout; // Has to be inside LOCK()s 00790 00791 MBED_ASSERT(_at != NULL); 00792 00793 if (!is_registered_psd() && !is_registered_csd() && !is_registered_eps()) { 00794 tr_info("Searching Network..."); 00795 // Enable the packet switched and network registration unsolicited result codes 00796 if (_at->send("AT+CREG=1") && _at->recv("OK") && 00797 _at->send("AT+CGREG=1") && _at->recv("OK")) { 00798 atSuccess = true; 00799 if (_at->send("AT+CEREG=1")) { 00800 _at->recv("OK"); 00801 // Don't check return value as this works for LTE only 00802 } 00803 00804 if (atSuccess) { 00805 // See if a PLMN is set by user 00806 if (_plmn[0] != 0) { 00807 tr_debug("Manual network registration to %s, please wait upto 3 minutes", _plmn); 00808 at_set_timeout(3*60*1000); 00809 _at->send("AT+COPS=1,2,\"%s\"", _plmn) && _at->recv("OK"); 00810 } else { 00811 tr_debug("Automatic network registration"); 00812 _at->send("AT+COPS=0") && _at->recv("OK"); 00813 } 00814 at_set_timeout(at_timeout); 00815 00816 // Query the registration status directly as well, 00817 // just in case 00818 if (_at->send("AT+CREG?") && _at->recv("OK")) { 00819 // Answer will be processed by URC 00820 } 00821 if (_at->send("AT+CGREG?") && _at->recv("OK")) { 00822 // Answer will be processed by URC 00823 } 00824 if (_at->send("AT+CEREG?")) { 00825 _at->recv("OK"); 00826 // Don't check return value as this works for LTE only 00827 } 00828 } 00829 } 00830 // Wait for registration to succeed 00831 at_set_timeout(1000); 00832 for (int waitSeconds = 0; !registered && (waitSeconds < 180); waitSeconds++) { 00833 registered = is_registered_psd() || is_registered_csd() || is_registered_eps(); 00834 _at->recv(UNNATURAL_STRING); 00835 } 00836 at_set_timeout(at_timeout); 00837 00838 if (registered) { 00839 // This should return quickly but sometimes the status field is not returned 00840 // so make the timeout short 00841 at_set_timeout(1000); 00842 if (_at->send("AT+COPS?") && _at->recv("+COPS: %*d,%*d,\"%*[^\"]\",%d\n", &status)) { 00843 set_rat(status); 00844 } 00845 at_set_timeout(at_timeout); 00846 } 00847 } else { 00848 registered = true; 00849 } 00850 00851 UNLOCK(); 00852 return registered; 00853 } 00854 00855 bool UbloxCellularBase::is_registered_csd() 00856 { 00857 return (_dev_info.reg_status_csd == CSD_REGISTERED) || 00858 (_dev_info.reg_status_csd == CSD_REGISTERED_ROAMING) || 00859 (_dev_info.reg_status_csd == CSD_CSFB_NOT_PREFERRED); 00860 } 00861 00862 bool UbloxCellularBase::is_registered_psd() 00863 { 00864 return (_dev_info.reg_status_psd == PSD_REGISTERED) || 00865 (_dev_info.reg_status_psd == PSD_REGISTERED_ROAMING); 00866 } 00867 00868 bool UbloxCellularBase::is_registered_eps() 00869 { 00870 return (_dev_info.reg_status_eps == EPS_REGISTERED) || 00871 (_dev_info.reg_status_eps == EPS_REGISTERED_ROAMING); 00872 } 00873 00874 // Perform deregistration. 00875 bool UbloxCellularBase::nwk_deregistration() 00876 { 00877 bool success = false; 00878 LOCK(); 00879 00880 MBED_ASSERT(_at != NULL); 00881 00882 if (_at->send("AT+COPS=2") && _at->recv("OK")) { 00883 _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING; 00884 _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING; 00885 _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING; 00886 success = true; 00887 } 00888 00889 UNLOCK(); 00890 return success; 00891 } 00892 00893 // Put the modem into its lowest power state. 00894 void UbloxCellularBase::deinit() 00895 { 00896 power_down(); 00897 _modem_initialised = false; 00898 } 00899 00900 // Set the PIN. 00901 void UbloxCellularBase::set_pin(const char *pin) { 00902 _pin = pin; 00903 } 00904 00905 // Enable or disable SIM pin checking. 00906 bool UbloxCellularBase::sim_pin_check_enable(bool enableNotDisable) 00907 { 00908 bool success = false;; 00909 LOCK(); 00910 00911 MBED_ASSERT(_at != NULL); 00912 00913 if (_pin != NULL) { 00914 if (_sim_pin_check_enabled && !enableNotDisable) { 00915 // Disable the SIM lock 00916 if (_at->send("AT+CLCK=\"SC\",0,\"%s\"", _pin) && _at->recv("OK")) { 00917 _sim_pin_check_enabled = false; 00918 success = true; 00919 } 00920 } else if (!_sim_pin_check_enabled && enableNotDisable) { 00921 // Enable the SIM lock 00922 if (_at->send("AT+CLCK=\"SC\",1,\"%s\"", _pin) && _at->recv("OK")) { 00923 _sim_pin_check_enabled = true; 00924 success = true; 00925 } 00926 } else { 00927 success = true; 00928 } 00929 } 00930 00931 UNLOCK(); 00932 return success; 00933 } 00934 00935 // Change the pin code for the SIM card. 00936 bool UbloxCellularBase::change_sim_pin(const char *pin) 00937 { 00938 bool success = false;; 00939 LOCK(); 00940 00941 MBED_ASSERT(_at != NULL); 00942 00943 // Change the SIM pin 00944 if ((pin != NULL) && (_pin != NULL)) { 00945 if (_at->send("AT+CPWD=\"SC\",\"%s\",\"%s\"", _pin, pin) && _at->recv("OK")) { 00946 _pin = pin; 00947 success = true; 00948 } 00949 } 00950 00951 UNLOCK(); 00952 return success; 00953 } 00954 00955 // Get the IMEI. 00956 bool UbloxCellularBase::get_imei(char *imei_to_send, int size) 00957 { 00958 bool success; 00959 LOCK(); 00960 00961 MBED_ASSERT(_at != NULL); 00962 00963 // International mobile equipment identifier 00964 // AT Command Manual UBX-13002752, section 4.7 00965 success = _at->send("AT+CGSN") && _at->recv("%15[^\n]\nOK\n", _dev_info.imei); 00966 tr_info("DevInfo: IMEI=%s", _dev_info.imei); 00967 00968 if (success) { 00969 memcpy(imei_to_send,_dev_info.imei,size); 00970 imei_to_send[size-1] = '\0'; 00971 } 00972 00973 UNLOCK(); 00974 return success; 00975 } 00976 00977 // Get the IMEI of the module. 00978 const char *UbloxCellularBase::imei() 00979 { 00980 return _dev_info.imei; 00981 } 00982 00983 // Get the Mobile Equipment ID (which may be the same as the IMEI). 00984 const char *UbloxCellularBase::meid() 00985 { 00986 return _dev_info.meid; 00987 } 00988 00989 // Get the IMSI of the SIM. 00990 const char *UbloxCellularBase::imsi() 00991 { 00992 // (try) to update the IMSI, just in case the SIM has changed 00993 get_imsi(); 00994 00995 return _dev_info.imsi; 00996 } 00997 00998 // Get the ICCID of the SIM. 00999 const char *UbloxCellularBase::iccid() 01000 { 01001 // (try) to update the ICCID, just in case the SIM has changed 01002 get_iccid(); 01003 01004 return _dev_info.iccid; 01005 } 01006 01007 // Get the RSSI in dBm. 01008 int UbloxCellularBase::rssi() 01009 { 01010 char buf[7] = {0}; 01011 int rssi = 0; 01012 int qual = 0; 01013 int rssiRet = 0; 01014 bool success; 01015 LOCK(); 01016 01017 MBED_ASSERT(_at != NULL); 01018 01019 success = _at->send("AT+CSQ") && _at->recv("+CSQ: %6[^\n]\nOK\n", buf); 01020 01021 if (success) { 01022 if (sscanf(buf, "%d,%d", &rssi, &qual) == 2) { 01023 // AT+CSQ returns a coded RSSI value and an RxQual value 01024 // For 2G an RSSI of 0 corresponds to -113 dBm or less, 01025 // an RSSI of 31 corresponds to -51 dBm or less and hence 01026 // each value is a 2 dB step. 01027 // For LTE the mapping is defined in the array rssiConvertLte[]. 01028 // For 3G the mapping to RSCP is defined in the array rscpConvert3G[] 01029 // and the RSSI value is then RSCP - the EC_NO_LEV number derived 01030 // by putting the qual number through qualConvert3G[]. 01031 if ((rssi >= 0) && (rssi <= 31)) { 01032 switch (_dev_info.rat) { 01033 case UTRAN: 01034 case HSDPA: 01035 case HSUPA: 01036 case HSDPA_HSUPA: 01037 // 3G 01038 if ((qual >= 0) && (qual <= 7)) { 01039 qual = qualConvert3G[qual]; 01040 rssiRet = rscpConvert3G[rssi]; 01041 rssiRet -= qual; 01042 } 01043 01044 break; 01045 case LTE: 01046 // LTE 01047 rssiRet = rssiConvertLte[rssi]; 01048 break; 01049 case GSM: 01050 case COMPACT_GSM: 01051 case EDGE: 01052 default: 01053 // GSM or assumed GSM if the RAT is not known 01054 rssiRet = -(113 - (rssi << 2)); 01055 break; 01056 } 01057 } 01058 } 01059 } 01060 01061 UNLOCK(); 01062 return rssiRet; 01063 } 01064 01065 //RAT should be set in a detached state (AT+COPS=2) 01066 bool UbloxCellularBase::set_modem_rat(RAT selected_rat, RAT preferred_rat, RAT second_preferred_rat) 01067 { 01068 bool success = false; 01069 char command[16] = {0x00}; 01070 01071 //check if modem is registered with network 01072 if (is_registered_csd() || is_registered_psd() || is_registered_eps()) { 01073 tr_error("RAT should only be set in detached state"); 01074 return false; 01075 } 01076 01077 if (preferred_rat != NOT_USED && second_preferred_rat != NOT_USED) { 01078 sprintf(command, "AT+URAT=%d,%d,%d", selected_rat, preferred_rat, second_preferred_rat); 01079 } else if (preferred_rat != NOT_USED) { 01080 sprintf(command, "AT+URAT=%d,%d", selected_rat, preferred_rat); 01081 } else if (second_preferred_rat != NOT_USED) { 01082 sprintf(command, "AT+URAT=%d,%d", selected_rat, second_preferred_rat); 01083 } else { 01084 sprintf(command, "AT+URAT=%d", selected_rat); 01085 } 01086 01087 LOCK(); 01088 if (_at->send(command) && _at->recv("OK")) { 01089 success = true; 01090 } else { 01091 tr_error("unable to set the specified RAT"); 01092 success = false; 01093 } 01094 UNLOCK(); 01095 01096 return success; 01097 } 01098 01099 bool UbloxCellularBase::get_modem_rat(RAT *selected_rat, RAT *preferred_rat, RAT *second_preferred_rat) 01100 { 01101 bool success = false; 01102 char buf[24] = {0x00}; 01103 01104 if (selected_rat == NULL || preferred_rat == NULL || second_preferred_rat == NULL) { 01105 tr_info("invalid pointers"); 01106 return false; 01107 } 01108 01109 MBED_ASSERT(_at != NULL); 01110 01111 *selected_rat = NOT_USED; 01112 *preferred_rat = NOT_USED; 01113 *second_preferred_rat = NOT_USED; 01114 01115 LOCK(); 01116 01117 if (_at->send("AT+URAT?") && _at->recv("%23[^\n]\nOK\n", buf)) { 01118 if (sscanf(buf, "+URAT: %d,%d,%d", (int*)selected_rat, (int*)preferred_rat, (int*)second_preferred_rat) == 3) { 01119 success = true; 01120 } else if (sscanf(buf, "+URAT: %d,%d", (int*)selected_rat, (int*)preferred_rat) == 2) { 01121 success = true; 01122 } else if (sscanf(buf, "+URAT: %d", (int*)selected_rat) == 1) { 01123 success = true; 01124 } 01125 } 01126 01127 UNLOCK(); 01128 return success; 01129 } 01130 01131 // Power down modem via AT interface. 01132 bool UbloxCellularBase::reboot_modem() 01133 { 01134 bool return_val = false; 01135 int at_timeout; 01136 LOCK(); 01137 01138 MBED_ASSERT(_at != NULL); 01139 01140 at_timeout = _at_timeout; // Has to be inside LOCK()s 01141 at_set_timeout(3*60*1000); //command has 3 minutes timeout 01142 tr_info("rebooting modem..."); 01143 01144 if (_at->send("AT+CFUN=15") && _at->recv("OK")) { 01145 tr_info("reboot successful"); 01146 return_val = true; 01147 } 01148 01149 _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING; 01150 _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING; 01151 _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING; 01152 _modem_initialised = false; 01153 at_set_timeout(at_timeout); 01154 UNLOCK(); 01155 01156 return return_val; 01157 } 01158 01159 #ifdef TARGET_UBLOX_C030_R41XM 01160 bool UbloxCellularBase::set_mno_profile(MNOProfile profile) 01161 { 01162 bool return_val = false; 01163 01164 MNOProfile mno_profile; 01165 if (get_mno_profile(&mno_profile)) { 01166 tr_info("Current MNO profile is: %d", (int)mno_profile); 01167 if (mno_profile != profile) { 01168 01169 if (is_registered_csd() || is_registered_psd() || is_registered_eps()) { 01170 tr_error("MNO profile should only be set in detached state"); 01171 return false; 01172 } 01173 01174 LOCK(); 01175 if (_at->send("AT+UMNOPROF=%d", profile) && _at->recv("OK")) { 01176 return_val = true; 01177 } else { 01178 tr_error("unable to set specified profile"); 01179 } 01180 UNLOCK(); 01181 01182 } else { 01183 return_val = true; 01184 } 01185 } else { 01186 tr_error("could not read MNO profile"); 01187 } 01188 01189 return return_val; 01190 } 01191 01192 bool UbloxCellularBase::get_mno_profile(MNOProfile *profile) 01193 { 01194 bool return_val = false; 01195 01196 if (profile == NULL) { 01197 return false; 01198 } 01199 01200 LOCK(); 01201 MBED_ASSERT(_at != NULL); 01202 01203 if ( (_at->send("AT+UMNOPROF?") && _at->recv("+UMNOPROF: %d", (int*)profile) && _at->recv("OK")) ) { 01204 return_val = true; 01205 } 01206 01207 UNLOCK(); 01208 return return_val; 01209 } 01210 #endif 01211 01212 // End of File 01213
Generated on Sun Jul 31 2022 21:56:42 by
1.7.2