Support for LISA-N101

Fork of C027_Support by u-blox

This is a variant of the C027 driver code for the C027N version, i.e. the one with the Neul/Huawei/u-blox Cellular Internet of Things module on board. The AT command interface for this module is entirely different to the AT interface for the other u-blox modules, hence this fork of the driver. Work is underway to rearchitect the original C027 driver so that a merge can be done.

Revision:
126:76b578ec2912
Parent:
125:72ca0c9a5a06
Child:
127:4df82b52f834
--- a/MDM.cpp	Thu Apr 23 12:05:40 2015 +0000
+++ b/MDM.cpp	Fri May 22 08:54:47 2015 +0000
@@ -1,4 +1,3 @@
-#include "mbed.h"
 #include "MDM.h"
 #ifdef TARGET_UBLOX_C027
  #include "C027_api.h"
@@ -6,7 +5,7 @@
 #include "MDMAPN.h"
                 
 #define PROFILE         "0"   //!< this is the psd profile used
-#define MAX_SIZE        128   //!< max expected messages
+#define MAX_SIZE        256   //!< max expected messages
 // num sockets
 #define NUMSOCKETS      (sizeof(_sockets)/sizeof(*_sockets))
 //! test if it is a socket is ok to use
@@ -106,6 +105,7 @@
     memset(_sockets, 0, sizeof(_sockets));
     for (int socket = 0; socket < NUMSOCKETS; socket ++)
         _sockets[socket].handle = SOCKET_ERROR;
+    _useNMI = false;
     _outstandingNMI = 0;
 #ifdef MDM_DEBUG
     _debugLevel = 1;
@@ -211,19 +211,21 @@
                 } else if (_dev.dev == DEV_LISA_N100) {
                     // Neul unsolicited responses  ----------------------------
                     // New Message Indication
-                    // +NMI:<length>,<data>
-                    if (sscanf(buf, "\r\n+NMI:%d,%s", &a, s) == 2) {
+                    // +NMI (alone, no data following, i.e. NMI=2 mode, not NMI=1 mode)
+                    if (strstr(cmd, "NMI\r\n") == cmd) {
                         _outstandingNMI++;
-                        TRACE("New Message Indication, length: %d\r\n", a);
+                        TRACE("New Message Indication, %d ready.\r\n", _outstandingNMI);
                     }
                     // Send Message Indication
                     // +SMI:<status>
                     if (sscanf(cmd, "SMI:%s", s) == 1) {
-                        TRACE("Send message status: %s\r\n", s);
+                        TRACE("Send message indication: %s\r\n", s);
                     }
                     // +GSN:<UUID>
-                    if (sscanf(cmd, "GSN:%s", s) == 1) {
-                        TRACE("Serial number is: %s\r\n", s);
+                    memset (s, 0, sizeof (s));
+                    // Don't use %s as it doesn't go beyond a space character and <NOT SET> is a possible value
+                    if (sscanf(cmd, "GSN:%[^\r\n]", s) == 1) {
+                        TRACE("UUID is: %s\r\n", s);
                     }
                     // +MGS:<status>
                     if (sscanf(cmd, "MGS:%s", s) == 1) {
@@ -259,7 +261,23 @@
                     }
                     // +DI:<string>
                     if (sscanf(cmd, "DI:%s", s) == 1) {
-                    // Do nothing, this will be spat out when debug is on anyway
+                        // Do nothing, these will be spat out at debug level 3
+                    }
+                    // +DEVELOPER_DCI_MCS: <2 or 4>
+                    memset (s, 0, sizeof (s));
+                    // Don't use %s as it doesn't go beyond a space character and NOT SET is a possible value
+                    if (sscanf(cmd, "DEVELOPER_DCI_MCS:%[^\r\n]", s) == 1) {
+                        TRACE("DEVELOPER_DCI_MCS: %s\r\n", s);
+                    }
+                    // +DEVELOPER_CHANNELS: <string>
+                    memset (s, 0, sizeof (s));
+                    // This is more complex to parse-out as sometimes the string is preceded with \r\n and
+                    // sometimes it isn't, also the portion we want to pick up includes spaces
+                    char * pStr = strstr (buf, "DEVELOPER_CHANNELS:");
+                    if (pStr != NULL) {
+                        if (sscanf(pStr + strlen ("DEVELOPER_CHANNELS:"), "%[^\r\n]", s) == 1) {
+                            TRACE("DEVELOPER_CHANNELS: %s\r\n", s);
+                        }
                     }
                 } else {
                     // GSM/UMTS Specific -------------------------------------------
@@ -353,8 +371,8 @@
 int MDMParser::_cbGmmString(int type, const char* buf, int len, char* str)
 {
     if (str && (type == TYPE_PLUS)) {
-        if (sscanf(buf, "\r\n+GMM:%s\r\n", str) == 1)
-            /*nothing*/;
+        if (sscanf(buf, "\r\n+GMM:%[^\r\n]", str) == 1) // Don't use %s as it doesn't go beyond a space character
+            /*nothing*/;                            // However, note that this means no null terminator is added
     }
     return WAIT;
 }
@@ -434,6 +452,7 @@
     
     INFO("Modem::init\r\n");
     // Attempt to get the manufacturer model string
+    memset (_dev.model, 0, sizeof (_dev.model)); // Necessary since the _cbGmmString call doesn't insert a terminator
     sendFormated("AT+GMM\r\n");
     if ((RESP_OK == waitFinalResp(_cbGmmString, _dev.model)) && (strstr (_dev.model, "Neul") == _dev.model)) {
         _dev.dev = DEV_LISA_N100;
@@ -443,12 +462,9 @@
         sendFormated("AT+GMI\r\n");
         if (RESP_OK != waitFinalResp(_cbGmiString, _dev.manu))
             goto failure;
-#ifndef DISABLE_NEUL_TEST_COMMANDS
-       /* In a phase 1 deployment special initialisation command should be inserted here, for example: */
-       /* sendFormated ("AT+DI=0\r\n");
-          if (RESP_OK != waitFinalResp())
-              goto failure; */
-#endif
+        sendFormated ("AT+DI=0\r\n");
+        if (RESP_OK != waitFinalResp())
+            goto failure;
         // get the sw version
         sendFormated("AT+GMR\r\n");
         if (RESP_OK != waitFinalResp(_cbGmrString, _dev.ver))
@@ -456,20 +472,27 @@
         // Don't set an error if this fails as it's not set on some modules
         sendFormated("AT+GSN\r\n");
         waitFinalResp(NULL, NULL, 1000);
-#ifdef C027N_USE_NMI
+        // Try to switch on NMI=2 (supported by phase 2 modules and beyond)
         // Switch on new message indications
         sendFormated("AT+NMI=2\r\n");
-        if (RESP_OK != waitFinalResp())
-            goto failure;
-#else
-        sendFormated("AT+NMI=0\r\n");
-        if (RESP_OK != waitFinalResp())
-            goto failure;
-#endif
+        if (RESP_OK == waitFinalResp()) {
+            _useNMI = true;
+        } else {  // If not OK, make sure it's switched off
+            sendFormated("AT+NMI=0\r\n");
+            if (RESP_OK != waitFinalResp())
+                goto failure;
+        }
         // Switch on send message indications
         sendFormated("AT+SMI=1\r\n");
         if (RESP_OK != waitFinalResp())
             goto failure;
+        // Query the scan range
+        sendFormated("AT+DEVELOPER_CHANNELS?\r\n");
+        // Don't set an error if this fails as it's not on phase 1 modules
+        waitFinalResp( NULL, NULL, 1000);
+        sendFormated("AT+DEVELOPER_DCI_MCS?\r\n");
+        // Don't set an error if this fails as it's not on phase 1 modules
+        waitFinalResp( NULL, NULL, 1000);
     } else {
         // 3GPP/CDMA modem init
         // echo off
@@ -696,10 +719,11 @@
         sendFormated("AT+RAS\r\n");
         if (RESP_OK != waitFinalResp(_cbRAS, &ok))
             goto failure;
-        _net.psd = REG_HOME;
-        sendFormated("AT+CSQ\r\n");
-        if (RESP_OK != waitFinalResp(_cbCSQN, &(_net.rssi)))
-            goto failure;
+        if (ok) {
+            _net.psd = REG_HOME;
+            if (!getRssi (&(_net.rssi)))
+                goto failure;
+        }
     } else {
         sendFormated("AT+CREG?\r\n");
         waitFinalResp();     // don't fail as service could be not subscribed 
@@ -779,7 +803,7 @@
         memcpy(status, &_net, sizeof(NetStatus));
     }
     if (_dev.dev == DEV_LISA_N100) {
-        ok = REG_DONE(_net.psd); // No CSD for the Neul modem
+        ok = REG_OK(_net.psd); // No CSD for the Neul modem
     } else {
         ok = REG_DONE(_net.csd) && REG_DONE(_net.psd);
     }
@@ -847,7 +871,7 @@
     if ((type == TYPE_PLUS) && connected) {
         // +RAS: <status>
         if (sscanf(buf, "\r\n+RAS:%s", status) == 1) {
-            if (strcmp ("CONNECTED\r\n", status) == 0) {
+            if (strcmp ("CONNECTED", status) == 0) {
                 *connected = true;
             }
             return RESP_OK;
@@ -1488,6 +1512,33 @@
 
 // ----------------------------------------------------------------
 // Neul AT command handling functions
+char * MDMParser::getVersion(void)
+{
+    return _dev.model;
+}
+
+bool MDMParser::sendMactest (int size, const char* buf)
+{
+    bool ok = false;
+    int sizeToSend;
+    char bufToSend[MAX_SIZE];
+
+    LOCK();
+
+    if (size <= (int) (sizeof(bufToSend) / 2))
+    {
+        sizeToSend = bytesToHexString (buf, size, bufToSend, sizeof(bufToSend));
+        sendFormated ("AT+MACTEST=%d,%.*s\r\n", size, sizeToSend, bufToSend);
+        if (RESP_OK == waitFinalResp()) {
+            ok = true;
+        }
+    }
+
+    UNLOCK();
+
+    return ok;
+}
+
 int MDMParser::_cbMGS (int type, const char* buf, int len, bool* ok)
 {
     *ok = false;
@@ -1528,7 +1579,7 @@
         if (ok) {
             ok = (RESP_OK == waitFinalResp(NULL));
             if (ok) {
-                ok = (RESP_OK == waitFinalResp(_cbSMI, &ok, 30000)) && ok;
+                ok = (RESP_OK == waitFinalResp(_cbSMI, &ok, 60000)) && ok;
             }
         }
     }
@@ -1572,75 +1623,61 @@
     return ok;
 }
 
-#ifdef C027N_USE_NMI
-
 bool MDMParser::datagramRecv(int* size, char* buf, int timeoutMs /* 10000 */)
 {
-    bool gotDatagram = false;
+    bool ok = false;
     MGRparam param;
     param.buf = buf;
     param.maxlen = *size;
     param.outlen = 0;
 
     LOCK();
-    if (_outstandingNMI == 0)
-    {
-        waitFinalResp(NULL, NULL, timeoutMs);
-    }
-    if (_outstandingNMI > 0)
+
+    if (_useNMI)
     {
-        gotDatagram = doMGR (size, buf, &param);
-        if (gotDatagram) {
-            _outstandingNMI--;
-            if (param.outlen > 0) {
-                gotDatagram = true;
+        // If NMI type 2 is supported (so NMI doesn't destroy the datagram),
+        // use the _outstandingNMI count and read the messages from the queue
+        if (_outstandingNMI == 0)
+        {
+            waitFinalResp(NULL, NULL, timeoutMs);
+        }
+        if (_outstandingNMI > 0)
+        {
+            ok = doMGR (size, buf, &param);
+            if (ok) {
+                _outstandingNMI--;
+                if (param.outlen > 0) {
+                    ok = true;
+                }
+            } else {
+                *size = 0;
             }
-        } else {
+        }
+    }
+    else
+    {
+        // If NMI type 2 is not supported, poll with MGR instead
+        Timer timer;
+
+        timer.start();
+        ok = true;
+        while (ok && (param.outlen == 0) && (timer.read_ms() < timeoutMs)) {
+            ok = doMGR (size, buf, &param);
+            if (ok && param.outlen == 0) {
+                wait_ms (500);
+            }
+        }
+        if (param.outlen == 0) {
+            ok = false;
             *size = 0;
         }
     }
-    UNLOCK();
 
-    return gotDatagram;
-}
-
-#else
-
-bool MDMParser::datagramRecv(int* size, char* buf, int timeoutMs /* 10000 */)
-{
-    bool ok = false;
-    MGRparam param;
-    Timer timer;
-    param.buf = buf;
-    param.maxlen = *size;
-    param.outlen = 0;
-
-// The correct way to do this would be to switch on NMI and wait, however
-// the NMI carries all the data with it, it's not just a flag, and so
-// the parser here would have to handle queues of data, which it can't.
-// So instead, just poll with AT+MGR until a mode of NMI comes along
-// which leaves the data in the buffer.
-
-    LOCK();
-    timer.start();
-    ok = true;
-    while (ok && (param.outlen == 0) && (timer.read_ms() < timeoutMs)) {
-        ok = doMGR (size, buf, &param);
-        if (ok && param.outlen == 0) {
-            wait_ms (500);
-        }
-    }
-    if (param.outlen == 0) {
-        ok = false;
-        *size = 0;
-    }
     UNLOCK();
 
     return ok;
 }
 
-#endif
-
 int MDMParser::_cbCSQN(int type, const char* buf, int len, int* rssi)
 {
     if ((type == TYPE_PLUS) && rssi) {
@@ -1986,6 +2023,7 @@
             { "\r\nBUSY\r\n",           NULL,               TYPE_BUSY       },
             { "\r\nNO ANSWER\r\n",      NULL,               TYPE_NOANSWER   },
             { "\r\n+",                  "\r\n",             TYPE_PLUS       },
+            { "+",                      "\r\n",             TYPE_PLUS       }, // Special for Neul modem as they sometimes drop off the \r\n at the start
             { "\r\n@",                  NULL,               TYPE_PROMPT     }, // Sockets
             { "\r\n>",                  NULL,               TYPE_PROMPT     }, // SMS
             { "\n>",                    NULL,               TYPE_PROMPT     }, // File
@@ -2121,4 +2159,4 @@
 
 int MDMUsb::getLine(char* buffer, int length)    { return NOT_FOUND; }
 
-#endif
\ No newline at end of file
+#endif