Add methods to return IMEI, MEID, IMSI, ICCID and RSSI.
Fork of ublox-cellular-base by
Revision 0:5cffef3371f6, committed 2017-06-12
- Comitter:
- RobMeades
- Date:
- Mon Jun 12 21:28:56 2017 +0000
- Child:
- 1:5aaecf2572dc
- Commit message:
- Initial commit, not yet compiling but I'm tired of doing everything in the on-line IDE so I need to publish to take the code off-line.
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UbloxCellularBase.cpp Mon Jun 12 21:28:56 2017 +0000 @@ -0,0 +1,881 @@ +/* Copyright (c) 2017 ublox Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "UARTSerial.h" +#include "APN_db.h" +#include "UbloxCellularBase.h" +#include "onboard_modem_api.h" +#ifdef FEATURE_COMMON_PAL +#include "mbed_trace.h" +#define TRACE_GROUP "UCB" +#else +#define tr_debug(format, ...) debug(format, ## __VA_ARGS__) +#define tr_info(format, ...) debug(format, ## __VA_ARGS__) +#define tr_warn(format, ...) debug(format, ## __VA_ARGS__) +#define tr_error(format, ...) debug(format, ## __VA_ARGS__) +#endif + +/********************************************************************** + * PRIVATE METHODS + **********************************************************************/ + +void UbloxCellularBase::set_nwk_reg_status_csd(int status) +{ + switch (status) { + case CSD_NOT_REGISTERED_NOT_SEARCHING: + case CSD_NOT_REGISTERED_SEARCHING: + tr_info("Not (yet) registered for circuit switched service"); + break; + case CSD_REGISTERED: + case CSD_REGISTERED_ROAMING: + tr_info("Registered for circuit switched service"); + break; + case CSD_REGISTRATION_DENIED: + tr_info("Circuit switched service denied"); + break; + case CSD_UNKNOWN_COVERAGE: + tr_info("Out of circuit switched service coverage"); + break; + case CSD_SMS_ONLY: + tr_info("SMS service only"); + break; + case CSD_SMS_ONLY_ROAMING: + tr_info("SMS service only"); + break; + case CSD_CSFB_NOT_PREFERRED: + tr_info("Registered for circuit switched service with CSFB not preferred"); + break; + default: + tr_info("Unknown circuit switched service registration status. %d", status); + break; + } + + _dev_info.reg_status_csd = static_cast<NetworkRegistrationStatusCsd>(status); +} + +void UbloxCellularBase::set_nwk_reg_status_psd(int status) +{ + switch (status) { + case PSD_NOT_REGISTERED_NOT_SEARCHING: + case PSD_NOT_REGISTERED_SEARCHING: + tr_info("Not (yet) registered for packet switched service"); + break; + case PSD_REGISTERED: + case PSD_REGISTERED_ROAMING: + tr_info("Registered for packet switched service"); + break; + case PSD_REGISTRATION_DENIED: + tr_info("Packet switched service denied"); + break; + case PSD_UNKNOWN_COVERAGE: + tr_info("Out of packet switched service coverage"); + break; + case PSD_EMERGENCY_SERVICES_ONLY: + tr_info("Limited access for packet switched service. Emergency use only."); + break; + default: + tr_info("Unknown packet switched service registration status. %d", status); + break; + } + + _dev_info.reg_status_psd = static_cast<NetworkRegistrationStatusPsd>(status); +} + +void UbloxCellularBase::set_nwk_reg_status_eps(int status) +{ + switch (status) { + case EPS_NOT_REGISTERED_NOT_SEARCHING: + case EPS_NOT_REGISTERED_SEARCHING: + tr_info("Not (yet) registered for EPS service"); + break; + case EPS_REGISTERED: + case EPS_REGISTERED_ROAMING: + tr_info("Registered for EPS service"); + break; + case EPS_REGISTRATION_DENIED: + tr_info("EPS service denied"); + break; + case EPS_UNKNOWN_COVERAGE: + tr_info("Out of EPS service coverage"); + break; + case EPS_EMERGENCY_SERVICES_ONLY: + tr_info("Limited access for EPS service. Emergency use only."); + break; + default: + tr_info("Unknown EPS service registration status. %d", status); + break; + } + + _dev_info.reg_status_eps = static_cast<NetworkRegistrationStatusEps>(status); +} + +void UbloxCellularBase::set_rat(int AcTStatus) +{ + switch (AcTStatus) { + case GSM: + case COMPACT_GSM: + tr_info("Connected in GSM"); + break; + case UTRAN: + tr_info("Connected to UTRAN"); + break; + case EDGE: + tr_info("Connected to EDGE"); + break; + case HSDPA: + tr_info("Connected to HSDPA"); + break; + case HSUPA: + tr_info("Connected to HSPA"); + break; + case HSDPA_HSUPA: + tr_info("Connected to HDPA/HSPA"); + break; + case LTE: + tr_info("Connected to LTE"); + break; + default: + tr_info("Unknown RAT %d", AcTStatus); + break; + } + + _dev_info.rat = static_cast<RadioAccessNetworkType>(AcTStatus); +} + +bool UbloxCellularBase::get_iccid() +{ + bool success; + LOCK(); + + MBED_ASSERT(_at != NULL); + + // Returns the ICCID (Integrated Circuit Card ID) of the SIM-card. + // ICCID is a serial number identifying the SIM. + // AT Command Manual UBX-13002752, section 4.12 + success = _at->send("AT+CCID") && _at->recv("+CCID: %20[^\n]\nOK\n", _dev_info.iccid); + tr_info("DevInfo: ICCID=%s", _dev_info.iccid); + + UNLOCK(); + return success; +} + +bool UbloxCellularBase::get_imsi() +{ + bool success; + LOCK(); + + MBED_ASSERT(_at != NULL); + + // International mobile subscriber identification + // AT Command Manual UBX-13002752, section 4.11 + success = _at->send("AT+CIMI") && _at->recv("%15[^\n]\nOK\n", _dev_info.imsi); + tr_info("DevInfo: IMSI=%s", _dev_info.imsi); + + UNLOCK(); + return success; +} + +bool UbloxCellularBase::get_imei() +{ + bool success; + LOCK(); + + MBED_ASSERT(_at != NULL); + + // International mobile equipment identifier + // AT Command Manual UBX-13002752, section 4.7 + success = _at->send("AT+CGSN") && _at->recv("%15[^\n]\nOK\n", _dev_info.imei); + tr_info("DevInfo: IMEI=%s", _dev_info.imei); + + UNLOCK(); + return success; +} + +bool UbloxCellularBase::get_meid() +{ + bool success; + LOCK(); + + MBED_ASSERT(_at != NULL); + + // Mobile equipment identifier + // AT Command Manual UBX-13002752, section 4.8 + success = _at->send("AT+GSN") && _at->recv("%18[^\n]\nOK\n", _dev_info.meid); + tr_info("DevInfo: MEID=%s", _dev_info.meid); + + UNLOCK(); + return success; +} + +bool UbloxCellularBase::set_sms() +{ + bool success = false; + char buf[32]; + LOCK(); + + MBED_ASSERT(_at != NULL); + + // Set up SMS format and enable URC + // AT Command Manual UBX-13002752, section 11 + if (_at->send("AT+CMGF=1") && _at->recv("OK")) { + tr_debug("SMS in text mode"); + if (_at->send("AT+CNMI=2,1") && _at->recv("OK")) { + tr_debug("SMS URC enabled"); + // Set to CS preferred since PS preferred doesn't work + // on some networks + if (_at->send("AT+CGSMS=1") && _at->recv("OK")) { + tr_debug("SMS set to CS preferred"); + success = true; + memset (buf, 0, sizeof (buf)); + if (_at->send("AT+CSCA?") && + _at->recv("+CSCA: \"%31[^\"]\"", buf) && + _at->recv("OK")) { + tr_info("SMS Service Centre address is \"%s\"", buf); + } + } + } + } + + UNLOCK(); + return success; +} + +void UbloxCellularBase::parser_abort_cb() +{ + _at->abort(); +} + +// Callback for CME ERROR and CMS ERROR. +void UbloxCellularBase::CMX_ERROR_URC() +{ + char buf[48]; + + if (read_at_to_char(buf, sizeof (buf), '\n') > 0) { + tr_debug("AT error %s", buf); + } + parser_abort_cb(); +} + +// Callback for circuit switched registration URC. +void UbloxCellularBase::CREG_URC() +{ + char buf[10]; + int status; + + // If this is the URC it will be a single + // digit followed by \n. If it is the + // answer to a CREG query, it will be + // a ": %d,%d\n" where the second digit + // indicates the status + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + if (read_at_to_char(buf, sizeof (buf), '\n') > 0) { + if (sscanf(buf, ": %*d,%d", &status) == 1) { + set_nwk_reg_status_csd(status); + } else if (sscanf(buf, ": %d", &status) == 1) { + set_nwk_reg_status_csd(status); + } + } +} + +// Callback for packet switched registration URC. +void UbloxCellularBase::CGREG_URC() +{ + char buf[10]; + int status; + + // If this is the URC it will be a single + // digit followed by \n. If it is the + // answer to a CGREG query, it will be + // a ": %d,%d\n" where the second digit + // indicates the status + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + if (read_at_to_char(buf, sizeof (buf), '\n') > 0) { + if (sscanf(buf, ": %*d,%d", &status) == 1) { + set_nwk_reg_status_psd(status); + } else if (sscanf(buf, ": %d", &status) == 1) { + set_nwk_reg_status_psd(status); + } + } +} + +// Callback for EPS registration URC. +void UbloxCellularBase::CEREG_URC() +{ + char buf[10]; + int status; + + // If this is the URC it will be a single + // digit followed by \n. If it is the + // answer to a CEREG query, it will be + // a ": %d,%d\n" where the second digit + // indicates the status + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + if (read_at_to_char(buf, sizeof (buf), '\n') > 0) { + if (sscanf(buf, ": %*d,%d", &status) == 1) { + set_nwk_reg_status_eps(status); + } else if (sscanf(buf, ": %d", &status) == 1) { + set_nwk_reg_status_eps(status); + } + } +} + +// Callback UMWI, just filtering it out. +void UbloxCellularBase::UMWI_URC() +{ + char buf[10]; + + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + read_at_to_char(buf, sizeof (buf), '\n'); +} + +/********************************************************************** + * PROTECTED METHODS + **********************************************************************/ + +#if MODEM_ON_BOARD +void UbloxCellularBase::modem_init() +{ + ::onboard_modem_init(); +} + +void UbloxCellularBase::modem_deinit() +{ + ::onboard_modem_deinit(); +} + +void UbloxCellularBase::modem_power_up() +{ + ::onboard_modem_power_up(); +} + +void UbloxCellularBase::modem_power_down() +{ + ::onboard_modem_power_down(); +} +#else +void UbloxCellularBase::modem_init() +{ + // Meant to be overridden +} + +void UbloxCellularBase::modem_deinit() +{ + // Meant to be overridden +} + +void UbloxCellularBase::modem_power_up() +{ + // Meant to be overridden +} + +void UbloxCellularBase::modem_power_down() +{ + // Mmeant to be overridden +} +#endif + +// Constructor. +// Note: to allow this base class to be inherited as a virtual base class +// by everyone, it takes no parameters. See also comment above classInit() +// in the header file. +UbloxCellularBase::UbloxCellularBase() +{ + _pin = NULL; + _at = NULL; + _at_timeout = AT_PARSER_TIMEOUT; + _fh = NULL; + _modem_initialised = false; + _sim_pin_check_enabled = false; + _debug_trace_on = false; + + _dev_info.dev = DEV_TYPE_NONE; + _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING; + _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING; + _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING; +} + +// Destructor. +UbloxCellularBase::~UbloxCellularBase() +{ + deinit(); + delete _at; + delete _fh; +} + +// Initialise the portions of this class that are parameterised. +void UbloxCellularBase::baseClassInit(PinName tx, PinName rx, + int baud, bool debug_on) +{ + // Only initialise ourselves if it's not already been done + if (_at == NULL) { + if (_debug_trace_on == false) { + _debug_trace_on = debug_on; + } + + // Set up File Handle for buffered serial comms with cellular module + // (which will be used by the AT parser) + _fh = new UARTSerial(tx, rx, baud); + + // Set up the AT parser + _at = new ATCmdParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE, + _at_timeout, _debug_trace_on); + + // Error cases, out of band handling + _at->oob("ERROR", callback(this, &UbloxCellularBase::parser_abort_cb)); + _at->oob("+CME ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC)); + _at->oob("+CMS ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC)); + + // Registration status, out of band handling + _at->oob("+CREG", callback(this, &UbloxCellularBase::CREG_URC)); + _at->oob("+CGREG", callback(this, &UbloxCellularBase::CGREG_URC)); + _at->oob("+CEREG", callback(this, &UbloxCellularBase::CEREG_URC)); + + // Capture the UMWI, just to stop it getting in the way + _at->oob("+UMWI", callback(this, &UbloxCellularBase::UMWI_URC)); + } +} + +// Set the AT parser timeout. +// Note: the AT interface should be locked before this is called. +void UbloxCellularBase::at_set_timeout(int timeout) { + + MBED_ASSERT(_at != NULL); + + _at_timeout = timeout; + _at->set_timeout(timeout); +} + +// Read up to size bytes from the AT interface up to a "end". +// Note: the AT interface should be locked before this is called. +int UbloxCellularBase::read_at_to_char(char * buf, int size, char end) +{ + int count = 0; + int x = 0; + + if (size > 0) { + for (count = 0; (count < size) && (x >= 0) && (x != end); count++) { + x = _at->getc(); + *(buf + count) = (char) x; + } + + count--; + *(buf + count) = 0; + + // Convert line endings: + // If end was '\n' (0x0a) and the preceding character was 0x0d, then + // overwrite that with null as well. + if ((count > 0) && (end == '\n') && (*(buf + count - 1) == '\x0d')) { + count--; + *(buf + count) = 0; + } + } + + return count; +} + +// Power up the modem. +// Enables the GPIO lines to the modem and then wriggles the power line in short pulses. +bool UbloxCellularBase::power_up() +{ + bool success = false; + int at_timeout; + LOCK(); + + at_timeout = _at_timeout; // Has to be inside LOCK()s + + MBED_ASSERT(_at != NULL); + + /* Initialize GPIO lines */ + tr_info("Powering up modem..."); + onboard_modem_init(); + /* Give modem a little time to settle down */ + wait_ms(250); + + for (int retry_count = 0; !success && (retry_count < 20); retry_count++) { + onboard_modem_power_up(); + wait_ms(500); + // Modem tends to spit out noise during power up - don't confuse the parser + _at->flush(); + at_set_timeout(1000); + if (_at->send("AT")) { + // C027 needs a delay here + wait_ms(100); + if (_at->recv("OK")) { + success = true; + } + } + at_set_timeout(at_timeout); + } + + + if (success) { + success = _at->send("ATE0;" // Turn off modem echoing + "+CMEE=2;" // Turn on verbose responses + "&K0" //turn off RTC/CTS handshaking + "+IPR=%d;" // Set baud rate + "&C1;" // Set DCD circuit(109), changes in accordance with the carrier detect status + "&D0", 115200) && // Set DTR circuit, we ignore the state change of DTR + _at->recv("OK"); + } + + if (!success) { + tr_error("Preliminary modem setup failed."); + } + + UNLOCK(); + return success; +} + +// Power down modem via AT interface. +void UbloxCellularBase::power_down() +{ + LOCK(); + + MBED_ASSERT(_at != NULL); + + // If we have been running, do a soft power-off first + if (_modem_initialised && (_at != NULL)) { + _at->send("AT+CPWROFF") && _at->recv("OK"); + } + + // Now do a hard power-off + onboard_modem_power_down(); + onboard_modem_deinit(); + + _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING; + _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING; + _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING; + + UNLOCK(); +} + +// Get the device ID. +bool UbloxCellularBase::set_device_identity(DeviceType *dev) +{ + char buf[20]; + bool success; + LOCK(); + + MBED_ASSERT(_at != NULL); + + success = _at->send("ATI") && _at->recv("%19[^\n]\nOK\n", buf); + + if (success) { + if (strstr(buf, "SARA-G35")) + *dev = DEV_SARA_G35; + else if (strstr(buf, "LISA-U200-03S")) + *dev = DEV_LISA_U2_03S; + else if (strstr(buf, "LISA-U2")) + *dev = DEV_LISA_U2; + else if (strstr(buf, "SARA-U2")) + *dev = DEV_SARA_U2; + else if (strstr(buf, "LEON-G2")) + *dev = DEV_LEON_G2; + else if (strstr(buf, "TOBY-L2")) + *dev = DEV_TOBY_L2; + else if (strstr(buf, "MPCI-L2")) + *dev = DEV_MPCI_L2; + } + + UNLOCK(); + return success; +} + +// Send initialisation AT commands that are specific to the device. +bool UbloxCellularBase::device_init(DeviceType dev) +{ + bool success = false; + LOCK(); + + MBED_ASSERT(_at != NULL); + + if ((dev == DEV_LISA_U2) || (dev == DEV_LEON_G2) || (dev == DEV_TOBY_L2)) { + success = _at->send("AT+UGPIOC=20,2") && _at->recv("OK"); + } else if ((dev == DEV_SARA_U2) || (dev == DEV_SARA_G35)) { + success = _at->send("AT+UGPIOC=16,2") && _at->recv("OK"); + } else { + success = true; + } + + UNLOCK(); + return success; +} + +// Get the SIM card going. +bool UbloxCellularBase::initialise_sim_card() +{ + bool success = false; + int retry_count = 0; + bool done = false; + LOCK(); + + MBED_ASSERT(_at != NULL); + + /* SIM initialisation may take a significant amount, so an error is + * kind of expected. We should retry 10 times until we succeed or timeout. */ + for (retry_count = 0; !done && (retry_count < 10); retry_count++) { + char pinstr[16]; + + if (_at->send("AT+CPIN?") && _at->recv("+CPIN: %15[^\n]\n", pinstr) && + _at->recv("OK")) { + done = true; + if (strcmp(pinstr, "SIM PIN") == 0) { + _sim_pin_check_enabled = true; + if (_at->send("AT+CPIN=\"%s\"", _pin)) { + if (_at->recv("OK")) { + tr_info("PIN correct"); + success = true; + } else { + tr_error("Incorrect PIN"); + } + } + } else if (strcmp(pinstr, "READY") == 0) { + _sim_pin_check_enabled = false; + tr_info("No PIN required"); + success = true; + } else { + tr_debug("Unexpected response from SIM: \"%s\"", pinstr); + } + } + + /* wait for a second before retry */ + wait_ms(1000); + } + + if (done) { + tr_info("SIM Ready."); + } else { + tr_error("SIM not ready."); + } + + UNLOCK(); + return success; +} + +/********************************************************************** + * PUBLIC METHODS + **********************************************************************/ + +// Initialise the modem. +bool UbloxCellularBase::init(const char *pin) +{ + MBED_ASSERT(_at != NULL); + + if (!_modem_initialised) { + if (power_up()) { + tr_info("Modem Ready."); + if (pin != NULL) { + _pin = pin; + } + if (initialise_sim_card()) { + if (set_device_identity(&_dev_info.dev) && // Set up device identity + device_init(_dev_info.dev) && // Initialise this device + get_iccid() && // Get integrated circuit ID of the SIM + get_imsi() && // Get international mobile subscriber information + get_imei() && // Get international mobile equipment identifier + get_meid() && // Probably the same as the IMEI + set_sms()) { + + // The modem is initialised. + _modem_initialised = true; + } + } + } + } + + return _modem_initialised; +} + +// Perform registration. +bool UbloxCellularBase::nwk_registration() +{ + bool atSuccess = false; + bool registered = false; + int status; + int at_timeout; + LOCK(); + + at_timeout = _at_timeout; // Has to be inside LOCK()s + + MBED_ASSERT(_at != NULL); + + if (!is_registered_psd() && !is_registered_csd() && !is_registered_eps()) { + tr_info("Searching Network..."); + // Enable the packet switched and network registration unsolicited result codes + if (_at->send("AT+CREG=1") && _at->recv("OK") && + _at->send("AT+CGREG=1") && _at->recv("OK")) { + atSuccess = true; + if (_at->send("AT+CEREG=1")) { + _at->recv("OK"); + // Don't check return value as this works for LTE only + } + + if (atSuccess) { + // See if we are already in automatic mode + if (_at->send("AT+COPS?") && _at->recv("+COPS: %d", &status) && + _at->recv("OK")) { + // If not, set it + if (status != 0) { + // Don't check return code here as there's not much + // we can do if this fails. + _at->send("AT+COPS=0") && _at->recv("OK"); + } + } + + // Query the registration status directly as well, + // just in case + if (_at->send("AT+CREG?") && _at->recv("OK")) { + // Answer will be processed by URC + } + if (_at->send("AT+CGREG?") && _at->recv("OK")) { + // Answer will be processed by URC + } + if (_at->send("AT+CEREG?")) { + _at->recv("OK"); + // Don't check return value as this works for LTE only + } + } + } + // Wait for registration to succeed + at_set_timeout(1000); + for (int waitSeconds = 0; !registered && (waitSeconds < 180); waitSeconds++) { + registered = is_registered_psd() || is_registered_csd() || is_registered_eps(); + _at->recv(UNNATURAL_STRING); + } + at_set_timeout(at_timeout); + + if (registered) { + // This should return quickly but sometimes the status field is not returned + // so make the timeout short + at_set_timeout(1000); + if (_at->send("AT+COPS?") && _at->recv("+COPS: %*d,%*d,\"%*[^\"]\",%d\n", &status)) { + set_rat(status); + } + at_set_timeout(at_timeout); + } + } else { + registered = true; + } + + UNLOCK(); + return registered; +} + +bool UbloxCellularBase::is_registered_csd() +{ + return (_dev_info.reg_status_csd == CSD_REGISTERED) || + (_dev_info.reg_status_csd == CSD_REGISTERED_ROAMING) || + (_dev_info.reg_status_csd == CSD_CSFB_NOT_PREFERRED); +} + +bool UbloxCellularBase::is_registered_psd() +{ + return (_dev_info.reg_status_psd == PSD_REGISTERED) || + (_dev_info.reg_status_psd == PSD_REGISTERED_ROAMING); +} + +bool UbloxCellularBase::is_registered_eps() +{ + return (_dev_info.reg_status_eps == EPS_REGISTERED) || + (_dev_info.reg_status_eps == EPS_REGISTERED_ROAMING); +} + +// Perform deregistration. +bool UbloxCellularBase::nwk_deregistration() +{ + bool success = false; + LOCK(); + + MBED_ASSERT(_at != NULL); + + if (_at->send("AT+COPS=2") && _at->recv("OK")) { + _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING; + _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING; + _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING; + success = true; + } + + UNLOCK(); + return success; +} + +// Put the modem into its lowest power state. +void UbloxCellularBase::deinit() +{ + power_down(); + _modem_initialised = false; +} + +// Set the PIN. +void UbloxCellularBase::set_pin(const char *pin) { + _pin = pin; +} + +// Enable or disable SIM pin checking. +bool UbloxCellularBase:: sim_pin_check_enable(bool enableNotDisable) +{ + bool success = false;; + LOCK(); + + MBED_ASSERT(_at != NULL); + + if (_pin != NULL) { + if (_sim_pin_check_enabled && !enableNotDisable) { + // Disable the SIM lock + if (_at->send("AT+CLCK=\"SC\",0,\"%s\"", _pin) && _at->recv("OK")) { + _sim_pin_check_enabled = false; + success = true; + } + } else if (!_sim_pin_check_enabled && enableNotDisable) { + // Enable the SIM lock + if (_at->send("AT+CLCK=\"SC\",1,\"%s\"", _pin) && _at->recv("OK")) { + _sim_pin_check_enabled = true; + success = true; + } + } else { + success = true; + } + } + + UNLOCK(); + return success; +} + +// Change the pin code for the SIM card. +bool UbloxCellularBase::change_sim_pin(const char *pin) +{ + bool success = false;; + LOCK(); + + MBED_ASSERT(_at != NULL); + + // Change the SIM pin + if ((pin != NULL) && (_pin != NULL)) { + if (_at->send("AT+CPWD=\"SC\",\"%s\",\"%s\"", _pin, pin) && _at->recv("OK")) { + _pin = pin; + success = true; + } + } + + UNLOCK(); + return success; +} + +// End of File +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UbloxCellularBase.h Mon Jun 12 21:28:56 2017 +0000 @@ -0,0 +1,381 @@ +/* Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UBLOX_CELLULAR_BASE_ +#define UBLOX_CELLULAR_BASE_ + +#include "mbed.h" +#include "ATCmdParser.h" +#include "FileHandle.h" + +/********************************************************************** + * CLASSES + **********************************************************************/ + +/** UbloxCellularBase class. + * + * This class provides all the base support for generic u-blox modems + * on C030 and C027 boards: module identification, power-up, network + * registration, etc. + */ +class UbloxCellularBase { + +public: + /** Circuit Switched network registration status (CREG Usage). + * UBX-13001820 - AT Commands Example Application Note (Section 7.10.3). + */ + typedef enum { + CSD_NOT_REGISTERED_NOT_SEARCHING = 0, + CSD_REGISTERED = 1, + CSD_NOT_REGISTERED_SEARCHING = 2, + CSD_REGISTRATION_DENIED = 3, + CSD_UNKNOWN_COVERAGE = 4, + CSD_REGISTERED_ROAMING = 5, + CSD_SMS_ONLY = 6, + CSD_SMS_ONLY_ROAMING = 7, + CSD_CSFB_NOT_PREFERRED = 9 + } NetworkRegistrationStatusCsd; + + /** Packet Switched network registration status (CGREG Usage). + * UBX-13001820 - AT Commands Example Application Note (Section 18.27.3). + */ + typedef enum { + PSD_NOT_REGISTERED_NOT_SEARCHING = 0, + PSD_REGISTERED = 1, + PSD_NOT_REGISTERED_SEARCHING = 2, + PSD_REGISTRATION_DENIED = 3, + PSD_UNKNOWN_COVERAGE = 4, + PSD_REGISTERED_ROAMING = 5, + PSD_EMERGENCY_SERVICES_ONLY = 8 + } NetworkRegistrationStatusPsd; + + /** EPS network registration status (CEREG Usage). + * UBX-13001820 - AT Commands Example Application Note (Section 18.36.3). + */ + typedef enum { + EPS_NOT_REGISTERED_NOT_SEARCHING = 0, + EPS_REGISTERED = 1, + EPS_NOT_REGISTERED_SEARCHING = 2, + EPS_REGISTRATION_DENIED = 3, + EPS_UNKNOWN_COVERAGE = 4, + EPS_REGISTERED_ROAMING = 5, + EPS_EMERGENCY_SERVICES_ONLY = 8 + } NetworkRegistrationStatusEps; + + /** Initialise the modem, ready for use. + * + * @param pin PIN for the SIM card. + * @return true if successful, otherwise false. + */ + bool init(const char *pin = 0); + + /** Perform registration with the network. + * + * @return true if successful, otherwise false. + */ + bool nwk_registration(); + + /** True if the modem is registered for circuit + * switched data, otherwise false. + */ + bool is_registered_csd(); + + /** True if the modem is registered for packet + * switched data, otherwise false. + */ + bool is_registered_psd(); + + /** True if the modem is registered for enhanced + * packet switched data (i.e. LTE and beyond), + * otherwise false. + */ + bool is_registered_eps(); + + /** Perform deregistration from the network. + * + * @return true if successful, otherwise false. + */ + bool nwk_deregistration(); + + /** Put the modem into its lowest power state. + */ + void deinit(); + + /** Set the PIN code for the SIM card. + * + * @param pin PIN for the SIM card. + */ + void set_pin(const char *pin); + + /** Enable or disable SIM pin checking. + * + * @param enableNotDisable true if SIM PIN checking is to be enabled, + * otherwise false. + * @return true if successful, otherwise false. + */ + bool sim_pin_check_enable(bool enableNotDisable); + + /** Change the SIM pin. + * + * @param new_pin the new PIN to use. + * @return true if successful, otherwise false. + */ + bool change_sim_pin(const char *new_pin); + +protected: + + #define OUTPUT_ENTER_KEY "\r" + + #if MBED_CONF_UBLOX_CELL_GEN_DRV_AT_PARSER_BUFFER_SIZE + #define AT_PARSER_BUFFER_SIZE MBED_CONF_UBLOX_CELL_GEN_DRV_AT_PARSER_BUFFER_SIZE + #else + #define AT_PARSER_BUFFER_SIZE 256 + #endif + + #if MBED_CONF_UBLOX_CELL_GEN_DRV_AT_PARSER_TIMEOUT + #define AT_PARSER_TIMEOUT MBED_CONF_UBLOX_CELL_GEN_DRV_AT_PARSER_TIMEOUT + #else + #define AT_PARSER_TIMEOUT 8*1000 // Milliseconds + #endif + + /** A string that would not normally be sent by the modem on the AT interface. + */ + #define UNNATURAL_STRING "\x01" + + /** Supported u-blox modem variants. + */ + typedef enum { + DEV_TYPE_NONE = 0, + DEV_SARA_G35, + DEV_LISA_U2, + DEV_LISA_U2_03S, + DEV_SARA_U2, + DEV_LEON_G2, + DEV_TOBY_L2, + DEV_MPCI_L2 + } DeviceType; + + /** Network registration status. + * UBX-13001820 - AT Commands Example Application Note (Section 4.1.4.5). + */ + typedef enum { + GSM = 0, + COMPACT_GSM = 1, + UTRAN = 2, + EDGE = 3, + HSDPA = 4, + HSUPA = 5, + HSDPA_HSUPA = 6, + LTE = 7 + } RadioAccessNetworkType; + + /** Info about the modem. + */ + typedef struct { + DeviceType dev; + char iccid[20 + 1]; //!< Integrated Circuit Card ID. + char imsi[15 + 1]; //!< International Mobile Station Identity. + char imei[15 + 1]; //!< International Mobile Equipment Identity. + char meid[18 + 1]; //!< Mobile Equipment IDentifier. + volatile RadioAccessNetworkType rat; //!< Type of network (e.g. 2G, 3G, LTE). + volatile NetworkRegistrationStatusCsd reg_status_csd; //!< Circuit switched attach status. + volatile NetworkRegistrationStatusPsd reg_status_psd; //!< Packet switched attach status. + volatile NetworkRegistrationStatusEps reg_status_eps; //!< Evolved Packet Switched (e.g. LTE) attach status. + } DeviceInfo; + + /* IMPORTANT: the variables below are available to + * classes that inherit this in order to keep things + * simple. However, ONLY this class should free + * any of the pointers, or there will be havoc. + */ + + /** Point to the instance of the AT parser in use. + */ + ATCmdParser *_at; + + /** The current AT parser timeout value. + */ + int _at_timeout; + + /** File handle used by the AT parser. + */ + FileHandle *_fh; + + /** The mutex resource. + */ + Mutex _mtx; + + /** General info about the modem as a device. + */ + DeviceInfo _dev_info; + + /** The SIM PIN to use. + */ + const char *_pin; + + /** Set to true to spit out debug traces. + */ + bool _debug_trace_on; + + /** True if the modem is ready register to the network, + * otherwise false. + */ + bool _modem_initialised; + + /** True it the SIM requires a PIN, otherwise false. + */ + bool _sim_pin_check_enabled; + + /** Sets the modem up for powering on + * + * modem_init() is equivalent to plugging in the device, e.g., attaching power and serial port. + * Uses onboard_modem_api.h where the implementation of onboard_modem_api is provided by the target. + */ + virtual void modem_init(); + + /** Sets the modem in unplugged state + * + * modem_deinit() will be equivalent to pulling the plug off of the device, i.e., detaching power + * and serial port. This puts the modem in lowest power state. + * Uses onboard_modem_api.h where the implementation of onboard_modem_api is provided by the target. + */ + virtual void modem_deinit(); + + /** Powers up the modem + * + * modem_power_up() is equivalent to pressing the soft power button. + * The driver may repeat this if the modem is not responsive to AT commands. + * Uses onboard_modem_api.h where the implementation of onboard_modem_api is provided by the target. + */ + virtual void modem_power_up(); + + /** Powers down the modem + * + * modem_power_down() is equivalent to turning off the modem by button press. + * Uses onboard_modem_api.h where the implementation of onboard_modem_api is provided by the target. + */ + virtual void modem_power_down(); + + /* Note: constructor and destructor protected so that this + * class can only ever be inherited, never used directly. + */ + UbloxCellularBase(); + ~UbloxCellularBase(); + + /** Initialise this class. + * + * @param tx the UART TX data pin to which the modem is attached. + * @param rx the UART RX data pin to which the modem is attached. + * @param baud the UART baud rate. + * @param debug_on true to switch AT interface debug on, otherwise false. + * + * Note: it would be more natural to do this in the constructor + * however, to avoid the diamond of death, this class is only + * every inherited virtually. Classes that are inherited virtually + * do not get passed parameters in their constructor and hence + * classInit() must be called separately by the first one to wake + * the beast. + */ + void baseClassInit(PinName tx = MDMTXD, + PinName rx = MDMRXD, + int baud = MBED_CONF_UBLOX_CELL_BAUD_RATE, + bool debug_on = false); + + /** Set the AT parser timeout. + */ + void at_set_timeout(int timeout); + + /** Read up to size characters from buf + * or until the character "end" is reached, overwriting + * the newline with 0 and ensuring a terminator + * in all cases. + * + * @param buf the buffer to write to. + * @param size the size of the buffer. + * @param end the character to stop at. + * @return the number of characters read, + * not including the terminator. + */ + int read_at_to_char(char * buf, int size, char end); + + /** Powers up the modem. + * + * @return true if successful, otherwise false. + */ + bool power_up(); + + /** Power down the modem. + */ + void power_down(); + + /** Lock a mutex when accessing the modem. + */ + void lock(void) { _mtx.lock(); } + + /** Helper to make sure that lock unlock pair is always balanced + */ + #define LOCK() { lock() + + /** Unlock the modem when done accessing it. + */ + void unlock(void) { _mtx.unlock(); } + + /** Helper to make sure that lock unlock pair is always balanced + */ + #define UNLOCK() } unlock() + + /** Set the device identity in _dev_info + * based on the ATI string returned by + * the module. + * + * @return true if dev is a known value, + * otherwise false. + */ + bool set_device_identity(DeviceType *dev); + + /** Perform any modem initialisation that is + * specialised by device type. + * + * @return true if successful, otherwise false. + */ + bool device_init(DeviceType dev); + + /** Set up the SIM. + * + * @return true if successful, otherwiss false. + */ + bool initialise_sim_card(); + +private: + + void set_nwk_reg_status_csd(int status); + void set_nwk_reg_status_psd(int status); + void set_nwk_reg_status_eps(int status); + void set_rat(int AcTStatus); + bool get_iccid(); + bool get_imsi(); + bool get_imei(); + bool get_meid(); + bool set_sms(); + void parser_abort_cb(); + void CMX_ERROR_URC(); + void CREG_URC(); + void CGREG_URC(); + void CEREG_URC(); + void UMWI_URC(); +}; + +#endif //_UBLOX_CELLULAR_DRIVER_GEN_BASE_ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_lib.json Mon Jun 12 21:28:56 2017 +0000 @@ -0,0 +1,8 @@ +{ + "name": "ublox-cell", + "config": { + "baud-rate": 115200, + "at-parser-buffer-size": 256, + "at-parser-timeout": 8000 + } +}