Fork for new features

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers UbloxCellularBase.cpp Source File

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