Add methods to return IMEI, MEID, IMSI, ICCID and RSSI.

Fork of ublox-cellular-base by u-blox

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