Curt Black / ublox-cellular-base

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                             /* 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 
00447     _dev_info.dev = DEV_TYPE_NONE;
00448     _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
00449     _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
00450     _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
00451 }
00452 
00453 // Destructor.
00454 UbloxCellularBase::~UbloxCellularBase()
00455 {
00456     deinit();
00457     delete _at;
00458     delete _fh;
00459 }
00460 
00461 // Initialise the portions of this class that are parameterised.
00462 void UbloxCellularBase::baseClassInit(PinName tx, PinName rx,
00463                                       int baud, bool debug_on)
00464 {
00465     // Only initialise ourselves if it's not already been done
00466     if (_at == NULL) {
00467         if (_debug_trace_on == false) {
00468             _debug_trace_on = debug_on;
00469         }
00470         _baud = baud;
00471 
00472         // Set up File Handle for buffered serial comms with cellular module
00473         // (which will be used by the AT parser)
00474         // Note: the UART is initialised to run no faster than 115200 because
00475         // the modems cannot reliably auto-baud at faster rates.  The faster
00476         // rate is adopted later with a specific AT command and the
00477         // UARTSerial rate is adjusted at that time
00478         if (baud != 115200) {
00479             baud = 115200;
00480         }
00481         _fh = new UARTSerial(tx, rx, baud);
00482 
00483         // Set up the AT parser
00484         _at = new ATCmdParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE,
00485                               _at_timeout, _debug_trace_on);
00486 
00487         // Error cases, out of band handling
00488         _at->oob("ERROR", callback(this, &UbloxCellularBase::parser_abort_cb));
00489         _at->oob("+CME ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC));
00490         _at->oob("+CMS ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC));
00491 
00492         // Registration status, out of band handling
00493         _at->oob("+CREG", callback(this, &UbloxCellularBase::CREG_URC));
00494         _at->oob("+CGREG", callback(this, &UbloxCellularBase::CGREG_URC));
00495         _at->oob("+CEREG", callback(this, &UbloxCellularBase::CEREG_URC));
00496 
00497         // Capture the UMWI, just to stop it getting in the way
00498         _at->oob("+UMWI", callback(this, &UbloxCellularBase::UMWI_URC));
00499     }
00500 }
00501 
00502 // Set the AT parser timeout.
00503 // Note: the AT interface should be locked before this is called.
00504 void UbloxCellularBase::at_set_timeout(int timeout) {
00505 
00506     MBED_ASSERT(_at != NULL);
00507 
00508     _at_timeout = timeout;
00509     _at->set_timeout(timeout);
00510 }
00511 
00512 // Read up to size bytes from the AT interface up to a "end".
00513 // Note: the AT interface should be locked before this is called.
00514 int UbloxCellularBase::read_at_to_char(char * buf, int size, char end)
00515 {
00516     int count = 0;
00517     int x = 0;
00518 
00519     if (size > 0) {
00520         for (count = 0; (count < size) && (x >= 0) && (x != end); count++) {
00521             x = _at->getc();
00522             *(buf + count) = (char) x;
00523         }
00524 
00525         count--;
00526         *(buf + count) = 0;
00527 
00528         // Convert line endings:
00529         // If end was '\n' (0x0a) and the preceding character was 0x0d, then
00530         // overwrite that with null as well.
00531         if ((count > 0) && (end == '\n') && (*(buf + count - 1) == '\x0d')) {
00532             count--;
00533             *(buf + count) = 0;
00534         }
00535     }
00536 
00537     return count;
00538 }
00539 
00540 // Power up the modem.
00541 // Enables the GPIO lines to the modem and then wriggles the power line in short pulses.
00542 bool UbloxCellularBase::power_up()
00543 {
00544     bool success = false;
00545     int at_timeout;
00546     LOCK();
00547 
00548     at_timeout = _at_timeout; // Has to be inside LOCK()s
00549 
00550     MBED_ASSERT(_at != NULL);
00551 
00552     /* Initialize GPIO lines */
00553     tr_info("Powering up modem...");
00554     modem_init();
00555     /* Give modem a little time to settle down */
00556     wait_ms(250);
00557 
00558     for (int retry_count = 0; !success && (retry_count < 20); retry_count++) {
00559         modem_power_up();
00560         wait_ms(500);
00561         // Modem tends to spit out noise during power up - don't confuse the parser
00562         _at->flush();
00563         at_set_timeout(1000);
00564         if (_at->send("AT")) {
00565             // C027 needs a delay here
00566             wait_ms(100);
00567             if (_at->recv("OK")) {
00568                 success = true;
00569             }
00570         }
00571         at_set_timeout(at_timeout);
00572     }
00573 
00574     if (success) {
00575         // Set the final baud rate
00576         if (_at->send("AT+IPR=%d", _baud) && _at->recv("OK")) {
00577             // Need to wait for things to be sorted out on the modem side
00578             wait_ms(100);
00579             ((UARTSerial *)_fh)->set_baud(_baud);
00580         }
00581         
00582         // Turn off modem echoing and turn on verbose responses
00583         success = _at->send("ATE0") && _at->recv("OK") &&
00584                   // The following commands are best sent separately
00585                   _at->send("AT+CMEE=2") && _at->recv("OK") &&
00586                   _at->send("AT&K0") && _at->recv("OK") && // Turn off RTC/CTS handshaking
00587                   _at->send("AT&C1") && _at->recv("OK") && // Set DCD circuit(109), changes in accordance with the carrier detect status
00588                   _at->send("AT&D0") && _at->recv("OK"); // Set DTR circuit, we ignore the state change of DTR
00589     }
00590 
00591     if (!success) {
00592         tr_error("Preliminary modem setup failed.");
00593     }
00594 
00595     UNLOCK();
00596     return success;
00597 }
00598 
00599 // Power down modem via AT interface.
00600 void UbloxCellularBase::power_down()
00601 {
00602     LOCK();
00603 
00604     MBED_ASSERT(_at != NULL);
00605 
00606     // If we have been running, do a soft power-off first
00607     if (_modem_initialised && (_at != NULL)) {
00608         _at->send("AT+CPWROFF") && _at->recv("OK");
00609     }
00610 
00611     // Now do a hard power-off
00612     modem_power_down();
00613     modem_deinit();
00614 
00615     _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
00616     _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
00617     _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
00618 
00619    UNLOCK();
00620 }
00621 
00622 // Get the device ID.
00623 bool UbloxCellularBase::set_device_identity(DeviceType *dev)
00624 {
00625     char buf[20];
00626     bool success;
00627     LOCK();
00628 
00629     MBED_ASSERT(_at != NULL);
00630 
00631     success = _at->send("ATI") && _at->recv("%19[^\n]\nOK\n", buf);
00632 
00633     if (success) {
00634         if (strstr(buf, "SARA-G35"))
00635             *dev = DEV_SARA_G35;
00636         else if (strstr(buf, "LISA-U200-03S"))
00637             *dev = DEV_LISA_U2_03S;
00638         else if (strstr(buf, "LISA-U2"))
00639             *dev = DEV_LISA_U2;
00640         else if (strstr(buf, "SARA-U2"))
00641             *dev = DEV_SARA_U2;
00642         else if (strstr(buf, "SARA-R4"))
00643             *dev = DEV_SARA_R4;
00644         else if (strstr(buf, "LEON-G2"))
00645             *dev = DEV_LEON_G2;
00646         else if (strstr(buf, "TOBY-L2"))
00647             *dev = DEV_TOBY_L2;
00648         else if (strstr(buf, "MPCI-L2"))
00649             *dev = DEV_MPCI_L2;
00650         else if (strstr(buf, "LARA-R2"))
00651             *dev = DEV_LARA_R2;
00652     }
00653 
00654     UNLOCK();
00655     return success;
00656 }
00657 
00658 // Send initialisation AT commands that are specific to the device.
00659 bool UbloxCellularBase::device_init(DeviceType dev)
00660 {
00661     bool success = false;
00662     LOCK();
00663 
00664     MBED_ASSERT(_at != NULL);
00665 
00666     if ((dev == DEV_LISA_U2) || (dev == DEV_LEON_G2) || (dev == DEV_TOBY_L2)) {
00667         success = _at->send("AT+UGPIOC=20,2") && _at->recv("OK");
00668     } else if ((dev == DEV_SARA_U2) || (dev == DEV_SARA_G35) || (dev == DEV_LARA_R2)) {
00669         success = _at->send("AT+UGPIOC=16,2") && _at->recv("OK");
00670     } else {
00671         success = true;
00672     }
00673 
00674     UNLOCK();
00675     return success;
00676 }
00677 
00678 // Get the SIM card going.
00679 bool UbloxCellularBase::initialise_sim_card()
00680 {
00681     bool success = false;
00682     int retry_count = 0;
00683     bool done = false;
00684     LOCK();
00685 
00686     MBED_ASSERT(_at != NULL);
00687 
00688     /* SIM initialisation may take a significant amount, so an error is
00689      * kind of expected. We should retry 10 times until we succeed or timeout. */
00690     for (retry_count = 0; !done && (retry_count < 10); retry_count++) {
00691         char pinstr[16];
00692 
00693         if (_at->send("AT+CPIN?") && _at->recv("+CPIN: %15[^\n]\n", pinstr) &&
00694             _at->recv("OK")) {
00695             done = true;
00696             if (strcmp(pinstr, "SIM PIN") == 0) {
00697                 _sim_pin_check_enabled = true;
00698                 if (_at->send("AT+CPIN=\"%s\"", _pin)) {
00699                     if (_at->recv("OK")) {
00700                         tr_info("PIN correct");
00701                         success = true;
00702                     } else {
00703                         tr_error("Incorrect PIN");
00704                     }
00705                 }
00706             } else if (strcmp(pinstr, "READY") == 0) {
00707                 _sim_pin_check_enabled = false;
00708                 tr_info("No PIN required");
00709                 success = true;
00710             } else {
00711                 tr_debug("Unexpected response from SIM: \"%s\"", pinstr);
00712             }
00713         }
00714 
00715         /* wait for a second before retry */
00716         wait_ms(1000);
00717     }
00718 
00719     if (done) {
00720         tr_info("SIM Ready.");
00721     } else {
00722         tr_error("SIM not ready.");
00723     }
00724 
00725     UNLOCK();
00726     return success;
00727 }
00728 
00729 /**********************************************************************
00730  * PUBLIC METHODS
00731  **********************************************************************/
00732 
00733 // Initialise the modem.
00734 bool UbloxCellularBase::init(const char *pin)
00735 {
00736     int x;
00737     MBED_ASSERT(_at != NULL);
00738 
00739     if (!_modem_initialised) {
00740         if (power_up()) {
00741             tr_info("Modem Ready.");
00742             if (pin != NULL) {
00743                 _pin = pin;
00744             }
00745             if (initialise_sim_card()) {
00746                 if (set_device_identity(&_dev_info.dev) && // Set up device identity
00747                     device_init(_dev_info.dev)) {// Initialise this device
00748                     // Get the integrated circuit ID of the SIM
00749                     if (get_iccid()) {
00750                         // Try a few times to get the IMSI (since on some modems this can
00751                         // take a while to be retrieved, especially if a SIM PIN
00752                         // was set)
00753                         for (x = 0; (x < 3) && !get_imsi(); x++) {
00754                             wait_ms(1000);
00755                         }
00756                         
00757                         if (x < 3) { // If we got the IMSI, can get the others
00758                             if (get_imei() && // Get international mobile equipment identifier
00759                                 get_meid() && // Probably the same as the IMEI
00760                                 set_sms()) { // And set up SMS
00761                                 // The modem is initialised.
00762                                 _modem_initialised = true;
00763                             }
00764                         }
00765                     }
00766                 }
00767             }
00768         }
00769     }
00770 
00771     return _modem_initialised;
00772 }
00773 
00774 // Perform registration.
00775 bool UbloxCellularBase::nwk_registration()
00776 {
00777     bool atSuccess = false;
00778     bool registered = false;
00779     int status;
00780     int at_timeout;
00781     LOCK();
00782 
00783     at_timeout = _at_timeout; // Has to be inside LOCK()s
00784 
00785     MBED_ASSERT(_at != NULL);
00786 
00787     if (!is_registered_psd() && !is_registered_csd() && !is_registered_eps()) {
00788         tr_info("Searching Network...");
00789         // Enable the packet switched and network registration unsolicited result codes
00790         if (_at->send("AT+CREG=1") && _at->recv("OK") &&
00791             _at->send("AT+CGREG=1") && _at->recv("OK")) {
00792             atSuccess = true;
00793             if (_at->send("AT+CEREG=1")) {
00794                 _at->recv("OK");
00795                 // Don't check return value as this works for LTE only
00796             }
00797 
00798             if (atSuccess) {
00799                 // See if we are already in automatic mode
00800                 if (_at->send("AT+COPS?") && _at->recv("+COPS: %d", &status) &&
00801                     _at->recv("OK")) {
00802                     // If not, set it
00803                     if (status != 0) {
00804                         // Don't check return code here as there's not much
00805                         // we can do if this fails.
00806                         _at->send("AT+COPS=0") && _at->recv("OK");
00807                     }
00808                 }
00809 
00810                 // Query the registration status directly as well,
00811                 // just in case
00812                 if (_at->send("AT+CREG?") && _at->recv("OK")) {
00813                     // Answer will be processed by URC
00814                 }
00815                 if (_at->send("AT+CGREG?") && _at->recv("OK")) {
00816                     // Answer will be processed by URC
00817                 }
00818                 if (_at->send("AT+CEREG?")) {
00819                     _at->recv("OK");
00820                     // Don't check return value as this works for LTE only
00821                 }
00822             }
00823         }
00824         // Wait for registration to succeed
00825         at_set_timeout(1000);
00826         for (int waitSeconds = 0; !registered && (waitSeconds < 180); waitSeconds++) {
00827             registered = is_registered_psd() || is_registered_csd() || is_registered_eps();
00828             _at->recv(UNNATURAL_STRING);
00829         }
00830         at_set_timeout(at_timeout);
00831 
00832         if (registered) {
00833             // This should return quickly but sometimes the status field is not returned
00834             // so make the timeout short
00835             at_set_timeout(1000);
00836             if (_at->send("AT+COPS?") && _at->recv("+COPS: %*d,%*d,\"%*[^\"]\",%d\n", &status)) {
00837                 set_rat(status);
00838             }
00839             at_set_timeout(at_timeout);
00840         }
00841     } else {
00842         registered = true;
00843     }
00844     
00845     UNLOCK();
00846     return registered;
00847 }
00848 
00849 bool UbloxCellularBase::is_registered_csd()
00850 {
00851   return (_dev_info.reg_status_csd == CSD_REGISTERED) ||
00852           (_dev_info.reg_status_csd == CSD_REGISTERED_ROAMING) ||
00853           (_dev_info.reg_status_csd == CSD_CSFB_NOT_PREFERRED);
00854 }
00855 
00856 bool UbloxCellularBase::is_registered_psd()
00857 {
00858     return (_dev_info.reg_status_psd == PSD_REGISTERED) ||
00859             (_dev_info.reg_status_psd == PSD_REGISTERED_ROAMING);
00860 }
00861 
00862 bool UbloxCellularBase::is_registered_eps()
00863 {
00864     return (_dev_info.reg_status_eps == EPS_REGISTERED) ||
00865             (_dev_info.reg_status_eps == EPS_REGISTERED_ROAMING);
00866 }
00867 
00868 // Perform deregistration.
00869 bool UbloxCellularBase::nwk_deregistration()
00870 {
00871     bool success = false;
00872     LOCK();
00873 
00874     MBED_ASSERT(_at != NULL);
00875 
00876     if (_at->send("AT+COPS=2") && _at->recv("OK")) {
00877         _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
00878         _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
00879         _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
00880         success = true;
00881     }
00882 
00883     UNLOCK();
00884     return success;
00885 }
00886 
00887 // Put the modem into its lowest power state.
00888 void UbloxCellularBase::deinit()
00889 {
00890     power_down();
00891     _modem_initialised = false;
00892 }
00893 
00894 // Set the PIN.
00895 void UbloxCellularBase::set_pin(const char *pin) {
00896     _pin = pin;
00897 }
00898 
00899 // Enable or disable SIM pin checking.
00900 bool UbloxCellularBase::sim_pin_check_enable(bool enableNotDisable)
00901 {
00902     bool success = false;;
00903     LOCK();
00904 
00905     MBED_ASSERT(_at != NULL);
00906 
00907     if (_pin != NULL) {
00908         if (_sim_pin_check_enabled && !enableNotDisable) {
00909             // Disable the SIM lock
00910             if (_at->send("AT+CLCK=\"SC\",0,\"%s\"", _pin) && _at->recv("OK")) {
00911                 _sim_pin_check_enabled = false;
00912                 success = true;
00913             }
00914         } else if (!_sim_pin_check_enabled && enableNotDisable) {
00915             // Enable the SIM lock
00916             if (_at->send("AT+CLCK=\"SC\",1,\"%s\"", _pin) && _at->recv("OK")) {
00917                 _sim_pin_check_enabled = true;
00918                 success = true;
00919             }
00920         } else {
00921             success = true;
00922         }
00923     }
00924 
00925     UNLOCK();
00926     return success;
00927 }
00928 
00929 // Change the pin code for the SIM card.
00930 bool UbloxCellularBase::change_sim_pin(const char *pin)
00931 {
00932     bool success = false;;
00933     LOCK();
00934 
00935     MBED_ASSERT(_at != NULL);
00936 
00937     // Change the SIM pin
00938     if ((pin != NULL) && (_pin != NULL)) {
00939         if (_at->send("AT+CPWD=\"SC\",\"%s\",\"%s\"", _pin, pin) && _at->recv("OK")) {
00940             _pin = pin;
00941             success = true;
00942         }
00943     }
00944 
00945     UNLOCK();
00946     return success;
00947 }
00948 
00949 // Get the IMEI.
00950 bool UbloxCellularBase::get_imei(char *imei_to_send, int size)
00951 {
00952     bool success;
00953     LOCK();
00954 
00955     MBED_ASSERT(_at != NULL);
00956 
00957     // International mobile equipment identifier
00958     // AT Command Manual UBX-13002752, section 4.7
00959     success = _at->send("AT+CGSN") && _at->recv("%15[^\n]\nOK\n", _dev_info.imei);
00960     tr_info("DevInfo: IMEI=%s", _dev_info.imei);
00961 
00962     if (success)    {
00963         memcpy(imei_to_send,_dev_info.imei,size);
00964         imei_to_send[size-1] = '\0';
00965     }
00966 
00967     UNLOCK();
00968     return success;
00969 }
00970 
00971 // Get the IMEI of the module.
00972 const char *UbloxCellularBase::imei()
00973 {
00974     return _dev_info.imei;
00975 }
00976 
00977 // Get the Mobile Equipment ID (which may be the same as the IMEI).
00978 const char *UbloxCellularBase::meid()
00979 {
00980     return _dev_info.meid;
00981 }
00982 
00983 // Get the IMSI of the SIM.
00984 const char *UbloxCellularBase::imsi()
00985 {
00986     // (try) to update the IMSI, just in case the SIM has changed
00987     get_imsi();
00988     
00989     return _dev_info.imsi;
00990 }
00991 
00992 // Get the ICCID of the SIM.
00993 const char *UbloxCellularBase::iccid()
00994 {
00995     // (try) to update the ICCID, just in case the SIM has changed
00996     get_iccid();
00997     
00998     return _dev_info.iccid;
00999 }
01000 
01001 // Get the RSSI in dBm.
01002 int UbloxCellularBase::rssi()
01003 {
01004     char buf[7] = {0};
01005     int rssi = 0;
01006     int qual = 0;
01007     int rssiRet = 0;
01008     bool success;
01009     LOCK();
01010 
01011     MBED_ASSERT(_at != NULL);
01012 
01013     success = _at->send("AT+CSQ") && _at->recv("+CSQ: %6[^\n]\nOK\n", buf);
01014 
01015     if (success) {
01016         if (sscanf(buf, "%d,%d", &rssi, &qual) == 2) {
01017             // AT+CSQ returns a coded RSSI value and an RxQual value
01018             // For 2G an RSSI of 0 corresponds to -113 dBm or less, 
01019             // an RSSI of 31 corresponds to -51 dBm or less and hence
01020             // each value is a 2 dB step.
01021             // For LTE the mapping is defined in the array rssiConvertLte[].
01022             // For 3G the mapping to RSCP is defined in the array rscpConvert3G[]
01023             // and the RSSI value is then RSCP - the EC_NO_LEV number derived
01024             // by putting the qual number through qualConvert3G[].
01025             if ((rssi >= 0) && (rssi <= 31)) {
01026                 switch (_dev_info.rat) {
01027                     case UTRAN:
01028                     case HSDPA:
01029                     case HSUPA:
01030                     case HSDPA_HSUPA:
01031                         // 3G
01032                         if ((qual >= 0) && (qual <= 7)) {
01033                             qual = qualConvert3G[qual];
01034                             rssiRet = rscpConvert3G[rssi];
01035                             rssiRet -= qual;
01036                         }
01037 
01038                         break;
01039                     case LTE:
01040                         // LTE
01041                         rssiRet = rssiConvertLte[rssi];
01042                         break;
01043                     case GSM:
01044                     case COMPACT_GSM:
01045                     case EDGE:
01046                     default:
01047                         // GSM or assumed GSM if the RAT is not known
01048                         rssiRet = -(113 - (rssi << 2));
01049                         break;
01050                 }
01051             }
01052         }
01053     }
01054 
01055     UNLOCK();
01056     return rssiRet;
01057 }
01058 
01059 // End of File
01060