support library for C027 helper functions for Buffer Pipes, Buffered Serial Port (rtos capable) and GPS parsing. It includes modem APIs for USSD, SMS and Sockets.

Dependents:   HTTPClient_Cellular_HelloWorld Cellular_HelloMQTT MbedSmartRestMain Car_Bon_car_module ... more

This library is intended to be used with u-blox products such as the C027 or a shield with u-blox cellular and GPS modules like the cellular and positioning shield from Embedded Artist.

For 2G/GSM and 3G/UMTS you need to:

  • have a SIM card and know its PIN number
  • need to know you network operators APN setting These setting should be passed to the connect or init and join functions. You can also extend the APN database in MDMAPN.h.

For CDMA products you need to make sure that you have provisioned and activated the modem with either Sprint or Verizon.

Revision:
75:ce6e12067d0c
Parent:
74:208e3e32d263
Child:
76:f7c3dd568dae
--- a/MDM.cpp	Thu May 15 22:20:42 2014 +0000
+++ b/MDM.cpp	Fri May 16 14:13:00 2014 +0000
@@ -11,27 +11,40 @@
 #define MAX_SIZE        128   //!< max expected messages
 //! test if it is a socket
 #define ISSOCKET(s)     (((s) >= 0) && ((s) < (sizeof(_sockets)/sizeof(*_sockets))))
-
+//! check for timeout
+#define TIMEOUT(t, ms)  ((ms != TIMEOUT_BLOCKING) && (ms < t.read_ms())) 
 
 #ifdef MDM_DEBUG
 void dumpAtCmd(const char* buf, int len)
 {
-    printf(" %3d \"", len);
+    ::printf(" %3d \"", len);
     while (len --) {
         char ch = *buf++;
-        if      (ch == '\r') puts("\\r");
-        else if (ch == '\n') puts("\\n");
-        else if (ch >= 0x20) putchar(ch);
-        else                 printf("\\x%02x", ch);
+        if ((ch > 0x1F) && (ch != 0x7F)) { // is printable
+            if      (ch == '%')  ::printf("%%");
+            else if (ch == '"')  ::printf("\\\"");
+            else if (ch == '\\') ::printf("\\\\");
+            else putchar(ch);
+        } else {
+            if      (ch == '\a') ::printf("\\a"); // BEL (0x07)
+            else if (ch == '\b') ::printf("\\b"); // Backspace (0x08)
+            else if (ch == '\t') ::printf("\\t"); // Horizontal Tab (0x09)
+            else if (ch == '\n') ::printf("\\n"); // Linefeed (0x0A)
+            else if (ch == '\v') ::printf("\\v"); // Vertical Tab (0x0B)
+            else if (ch == '\f') ::printf("\\f"); // Formfeed (0x0C)
+            else if (ch == '\r') ::printf("\\r"); // Carriage Return (0x0D)
+            else                 ::printf("\\x%02x", ch);
+        }
     }
-    puts("\"\r\n");
+    ::printf("\"\r\n");
 }
  
- #define INFO  (_debugLevel >= 1) ? : printf
- #define TRACE (_debugLevel >= 2) ? : printf
+ #define ERROR(fmt) (_debugLevel < 0) ? : ::printf(RED(fmt))
+ #define INFO(fmt)  (_debugLevel < 1) ? : ::printf(GRE(fmt))
+ #define TRACE(...) (_debugLevel < 2) ? : ::printf(__VA_ARGS__)
  
  #if 1 // colored terminal output using ANSI escape sequences
-  #define COL(c,t) "\33[" c t "\33[" "39m"
+  #define COL(c,t) "\033[" c t "\033[" "39m"
  #else
   #define COL(c,t) t
  #endif
@@ -46,6 +59,7 @@
  
 #else
  
+ #define ERROR(...) (void)0 // no tracing
  #define INFO(...)  (void)0 // no tracing
  #define TRACE(...) (void)0 // no tracing
 
@@ -80,7 +94,7 @@
 {
 #ifdef MDM_DEBUG
     if (_debugLevel >= 3) {
-        printf("%10.3f AT send    ", _debugTime.read_ms()*0.001);
+        ::printf("%10.3f AT send    ", _debugTime.read_ms()*0.001);
         dumpAtCmd(buf,len);
     }
 #endif
@@ -117,16 +131,13 @@
                             (type == TYPE_PLUS)   ? CYA(" + ") : 
                             (type == TYPE_PROMPT) ? BLU(" > ") : 
                                                         "..."  ;
-            printf("%10.3f AT read %s", _debugTime.read_ms()*0.001, s);
+            ::printf("%10.3f AT read %s", _debugTime.read_ms()*0.001, s);
             dumpAtCmd(buf, len);
         }
 #endif        
         if ((ret != WAIT) && (ret != NOT_FOUND))
         {
             int type = TYPE(ret);
-            if (type == TYPE_OK)        return RESP_OK;
-            if (type == TYPE_ERROR)     return RESP_ERROR;
-            if (type == TYPE_PROMPT)    return RESP_PROMPT;
             // handle unsolicited commands here
             if (type == TYPE_PLUS) {
                 const char* cmd = buf+3;
@@ -208,14 +219,14 @@
                 if (WAIT != ret)
                     return ret; 
             }
+            if (type == TYPE_OK)        return RESP_OK;
+            if (type == TYPE_ERROR)     return RESP_ERROR;
+            if (type == TYPE_PROMPT)    return RESP_PROMPT;
         }
         // relax a bit
         RELAX_MS(10); 
     }
-    while ((timeout_ms == TIMEOUT_BLOCKING) || 
-           (timer.read_ms() < timeout_ms));
-    timer.stop();
-    timer.reset();
+    while (!TIMEOUT(timer, timeout_ms));
     return WAIT;
 }
 
@@ -244,28 +255,17 @@
             const char* apn, const char* username, const char* password,
             PinName pn)
 {
-    DevStatus devStatus = {};
-    bool mdmOk = init(simpin, &devStatus, pn);  
+    bool ok = init(simpin, NULL, pn);  
 #ifdef MDM_DEBUG
-    if (_debugLevel >= 1) dumpDevStatus(&devStatus);
+    if (_debugLevel >= 1) dumpDevStatus(&_dev);
 #endif
-    if (!mdmOk)
+    if (!ok)
         return false;
-    // wait until we are connected
-    int i = 180;
-    NetStatus netStatus = {};
-    INFO("Modem::register\r\n");
-    while (!checkNetStatus(&netStatus))
-    {
-        if ((netStatus.reg == REG_DENIED) || (i == 0))
-            break;;
-        i --;
-        RELAX_MS(1000);
-    }
+    ok = registerNet();
 #ifdef MDM_DEBUG
-    if (_debugLevel >= 1) dumpNetStatus(&netStatus);
+    if (_debugLevel >= 1) dumpNetStatus(&_net);
 #endif
-    if ((netStatus.reg == REG_DENIED) || (i == 0))
+    if (!ok)
         return false;
     IP ip = join(apn,username,password);
 #ifdef MDM_DEBUG
@@ -292,9 +292,12 @@
             if(RESP_OK == waitFinalResp(NULL,NULL,500))
                 break;
         }
-        if (i < 0)
+        if (i < 0) {
+            ERROR("No Reply from Modem");
             return false;
+        }
     }
+    
     INFO("Modem::init\r\n");
     // echo off
     sendFormated("AT E0\r\n");
@@ -363,9 +366,12 @@
             if ((RESP_OK != ret) && (RESP_ERROR != ret))
                 return false;
             // Enter PIN if needed
-            if (_dev.sim == SIM_PIN) {
+            if (_dev.sim == SIM_MISSING) {
+                ERROR("SIM not inserted\r\n");
+                return false;
+            } else if (_dev.sim == SIM_PIN) {
                 if (!simpin) {
-                    INFO("SIM PIN not available\r\n");
+                    ERROR("SIM PIN not available\r\n");
                     return false;
                 }
                 sendFormated("AT+CPIN=%s\r\n", simpin);
@@ -398,12 +404,6 @@
         sendFormated("AT+CGSN\r\n");
         if (RESP_OK != waitFinalResp(_cbString, _dev.imei))
             return false;
-#if 0
-        // Configure New message indication
-        sendFormated("AT+CNMI=2,1,0,0,0\r\n");
-        if (RESP_OK != waitFinalResp())
-            return false;
-#endif
         // enable power saving
         if (_dev.lpm != LPM_DISABLED) {
              // enable power saving (requires flow control, cts at least)
@@ -418,7 +418,7 @@
     if (RESP_OK != waitFinalResp())
         return false;
     // setup new message indication
-    sendFormated("AT+CNMI=1,1\r\n");
+    sendFormated("AT+CNMI=2,1\r\n");
     if (RESP_OK != waitFinalResp())
         return false;
     // Request IMSI (International Mobile Subscriber Identification)
@@ -462,10 +462,14 @@
 
 int MDMParser::_cbCPIN(int type, const char* buf, int len, Sim* sim)
 {
-    if ((type == TYPE_PLUS) && sim){
-        char s[16];
-        if (sscanf(buf, "\r\n+CPIN: %[^\r]\r<n", s) >= 1) {
-            *sim = (strcmp("READY", s) == 0) ? SIM_READY : SIM_PIN;
+    if (sim) {
+        if (type == TYPE_PLUS){
+            char s[16];
+            if (sscanf(buf, "\r\n+CPIN: %[^\r]\r\n", s) >= 1)
+                *sim = (0 == strcmp("READY", s)) ? SIM_READY : SIM_PIN;
+        } else if (type == TYPE_ERROR) {
+            if (strstr(buf, "+CME ERROR: SIM not inserted"))
+                *sim = SIM_MISSING;
         }
     }
     return WAIT;
@@ -480,53 +484,69 @@
     return WAIT;
 }
 
+bool MDMParser::registerNet(NetStatus* status /*= NULL*/, int timeout_ms /*= 180000*/) 
+{
+    Timer timer;
+    timer.start();
+    INFO("Modem::register\r\n");
+    while (!checkNetStatus(status) && !TIMEOUT(timer, timeout_ms))
+        RELAX_MS(1000);
+    if (_net.reg == REG_DENIED)
+        ERROR("Network Registration Denied\r\n");
+    return (_net.reg == REG_ROAMING) || (_net.reg == REG_HOME);
+}
+
 bool MDMParser::checkNetStatus(NetStatus* status /*= NULL*/)
 {
+    memset(&_net, 0, sizeof(_net));
+    _net.lac = 0xFFFF;
+    _net.ci = 0xFFFFFFFF;
     // check registration
     sendFormated("AT+CREG?\r\n");
     if (RESP_OK != waitFinalResp())
         return false;
-    if ((_net.reg != REG_ROAMING) && (_net.reg != REG_HOME))
-        return false;
-    // check modem specific status messages 
-    if (_dev.dev == DEV_LISA_C200) {
-        sendFormated("AT+CSS?\r\n");
-        if (RESP_OK != waitFinalResp())
-            return false;
-        // get the Telephone number
-        sendFormated("AT$MDN?\r\n");
-        if (RESP_OK != waitFinalResp(_cbString, _net.num))
-            return false;
-        // check if we have a Mobile Directory Number
-        if (memcmp(_net.num, "0000", 4) == 0)
-            return false;
-        // get the the Network access identifier string
-        char nai[64];
-        sendFormated("AT$QCMIPNAI?\r\n");
-        if (RESP_OK != waitFinalResp(_cbString, nai))
+    if ((_net.reg == REG_ROAMING) || (_net.reg == REG_HOME))
+    {
+        // check modem specific status messages 
+        if (_dev.dev == DEV_LISA_C200) {
+            sendFormated("AT+CSS?\r\n");
+            if (RESP_OK != waitFinalResp())
+                return false;
+            // get the Telephone number
+            sendFormated("AT$MDN?\r\n");
+            if (RESP_OK != waitFinalResp(_cbString, _net.num))
+                return false;
+            // check if we have a Mobile Directory Number
+            if (!*_net.num || (memcmp(_net.num, "0000", 4) == 0))
+                return false;
+            // get the the Network access identifier string
+            char nai[64];
+            sendFormated("AT$QCMIPNAI?\r\n");
+            if (RESP_OK != waitFinalResp(_cbString, nai))
+                return false;
+        } else {
+            // check GPRS attach status
+            sendFormated("AT+CGATT?\r\n");
+            if (RESP_OK != waitFinalResp(_cbCGATT, &_net.gprs))
+                return false;
+            // check operator selection 
+            sendFormated("AT+COPS?\r\n");
+            if (RESP_OK != waitFinalResp(_cbCOPS, &_net))
+                return false;
+            // Returns the MSISDNs related to this subscriber
+            sendFormated("AT+CNUM\r\n");
+            if (RESP_OK != waitFinalResp(_cbCNUM, _net.num))
+                return false;
+        }  
+        // Returns the signal strength indication
+        sendFormated("AT+CSQ\r\n");
+        if (RESP_OK != waitFinalResp(_cbCSQ, &_net))
             return false;
-    } else {
-        // check GPRS attach status
-        sendFormated("AT+CGATT?\r\n");
-        if (RESP_OK != waitFinalResp(_cbCGATT, &_net.gprs))
-            return false;
-        // check operator selection 
-        sendFormated("AT+COPS?\r\n");
-        if (RESP_OK != waitFinalResp(_cbCOPS, &_net))
-            return false;
-        // Returns the MSISDNs related to this subscriber
-        sendFormated("AT+CNUM\r\n");
-        if (RESP_OK != waitFinalResp(_cbCNUM, _net.num))
-            return false;
-    }  
-    // Returns the signal strength indication
-    sendFormated("AT+CSQ\r\n");
-    if (RESP_OK != waitFinalResp(_cbCSQ, &_net))
-        return false;
+    }
     if (status) {
         memcpy(status, &_net, sizeof(NetStatus));
     }
-    return true;
+    return ((_net.reg == REG_HOME) || (_net.reg == REG_ROAMING) || (_net.reg == REG_DENIED));
 }
 
 int MDMParser::_cbCGATT(int type, const char* buf, int len, Gprs* gprs)
@@ -636,7 +656,7 @@
         // Activate the profile and make connection
         sendFormated("AT+UPSDA=" PROFILE ",3\r\n");
         if (RESP_OK != waitFinalResp(NULL,NULL,150*1000)) {
-            INFO("Your modem APN/password/username may be wrong\r\n");
+            ERROR("Your modem APN/password/username may be wrong\r\n");
             return NOIP;
         }
         //Get local IP address
@@ -900,18 +920,14 @@
             cnt += blk;
             buf += blk;
             _sockets[socket].pending -= blk;
-        } else if ((_sockets[socket].state == SOCK_CONNECTED) && (
-                   (_sockets[socket].timeout_ms == TIMEOUT_BLOCKING) || 
-                   (timer.read_ms() < _sockets[socket].timeout_ms))){
+        } else if ((_sockets[socket].state == SOCK_CONNECTED) && 
+                   !TIMEOUT(timer, _sockets[socket].timeout_ms)) {
             // allow to receive unsolicited commands 
             waitFinalResp(NULL, NULL, 10);
         } else {
             len = 0; // no more data and socket closed or timed-out
         }
     }
-    
-    timer.stop();
-    timer.reset();
     return cnt;
 }
 
@@ -957,13 +973,11 @@
             cnt += blk;
             buf += blk;
             _sockets[socket].pending -= blk;
-        } else if ((_sockets[socket].timeout_ms == TIMEOUT_BLOCKING) || 
-                   (timer.read_ms() < _sockets[socket].timeout_ms)) {
+        } else if (!TIMEOUT(timer, _sockets[socket].timeout_ms)) {
             // allow to receive unsolicited commands 
             waitFinalResp(NULL, NULL, 10);
-        } else {
+        } else
             len = 0; // no more data and socket closed or timed-out
-        }
     }
     timer.stop();
     timer.reset();
@@ -1084,14 +1098,14 @@
 void MDMParser::dumpDevStatus(MDMParser::DevStatus* status, 
             _DPRINT dprint, void* param) 
 {
-    dprint(param, "Modem Device Status:\r\n");
+    dprint(param, "Modem::devStatus\r\n");
     const char* txtDev[] = { "Unknown", "SARA-G350", "LISA-U200", "LISA-C200" };
     if (status->dev < sizeof(txtDev)/sizeof(*txtDev) && (status->dev != MDMParser::DEV_UNKNOWN))
         dprint(param, "  Device:       %s\r\n", txtDev[status->dev]);
     const char* txtLpm[] = { "Disabled", "Enabled", "Active" };
     if (status->lpm < sizeof(txtLpm)/sizeof(*txtLpm))
         dprint(param, "  Power Save:   %s\r\n", txtLpm[status->lpm]);
-    const char* txtSim[] = { "Unknown", "Pin", "Ready" };
+    const char* txtSim[] = { "Unknown", "Missing", "Pin", "Ready" };
     if (status->sim < sizeof(txtSim)/sizeof(*txtSim) && (status->sim != MDMParser::SIM_UNKNOWN))
         dprint(param, "  SIM:          %s\r\n", txtSim[status->sim]);
     if (*status->ccid)  
@@ -1113,7 +1127,7 @@
 void MDMParser::dumpNetStatus(MDMParser::NetStatus *status,
             _DPRINT dprint, void* param)
 {
-    dprint(param, "Modem Network Status:\r\n");
+    dprint(param, "Modem::netStatus\r\n");
     const char* txtReg[] = { "Unknown", "Denied", "None", "Home", "Roaming" };
     if (status->reg < sizeof(txtReg)/sizeof(*txtReg) && (status->reg != MDMParser::REG_UNKNOWN))
         dprint(param, "  Registration:       %s\r\n", txtReg[status->reg]);
@@ -1141,7 +1155,7 @@
             _DPRINT dprint, void* param) 
 {
     if (ip != NOIP)
-        dprint(param, "Modem IP Address: " IPSTR "\r\n", IPNUM(ip));
+        dprint(param, "Modem:IP " IPSTR "\r\n", IPNUM(ip));
 }
     
 // ----------------------------------------------------------------