Base class for the ublox-xxx-cellular-xxx classes. Cannot be used standalone, only inherited by classes that do properly useful stuff. Or, to put it another way, if you are using any of the ublox-xxx-cellular-xxx classes, you will need this class also.
Dependents: example-ublox-cellular-interface example-ublox-cellular-driver-gen HelloMQTT example-ublox-cellular-interface_r410M ... more
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
+ }
+}
u-blox