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.

Fork of C027_Support by u-blox

Files at this revision

API Documentation at this revision

Comitter:
mazgch
Date:
Tue Apr 08 09:15:37 2014 +0000
Parent:
19:2b5d097ca15d
Child:
22:29322c22577e
Commit message:
improved modem library (network, sockets, sms, ussd)

Changed in this revision

GPS.cpp Show annotated file Show diff for this revision Revisions of this file
MDM.cpp Show annotated file Show diff for this revision Revisions of this file
MDM.h Show annotated file Show diff for this revision Revisions of this file
Pipe.h Show annotated file Show diff for this revision Revisions of this file
--- a/GPS.cpp	Mon Mar 24 07:38:05 2014 +0000
+++ b/GPS.cpp	Tue Apr 08 09:15:37 2014 +0000
@@ -12,28 +12,31 @@
     while (len > 0)
     {
         // NMEA protocol
+        pipe->set(unkn);
         int nmea = _parseNmea(pipe,len);
         if ((nmea != NOT_FOUND) && (unkn > 0))  
-            return unkn;
+            return pipe->get(buf,unkn);
         if (nmea == WAIT && fr)                       
             return WAIT;
         if (nmea > 0)                           
             return NMEA | pipe->get(buf,nmea);
         // UBX protocol
+        
+        pipe->set(unkn);
         int ubx = _parseUbx(pipe,len);
         if ((ubx != NOT_FOUND) && (unkn > 0))   
-            return unkn;
+            return pipe->get(buf,unkn);
         if (ubx == WAIT && fr)                        
             return WAIT;
         if (ubx > 0)                            
             return UBX | pipe->get(buf,ubx);
+        
         // UNKNOWN
-        *buf++ = pipe->getc();
         unkn ++;
         len--;
     }
-    if (unkn != NOT_FOUND)                      
-        return unkn; 
+    if (unkn > 0)                      
+        return pipe->get(buf,unkn); 
     return WAIT;
 }
 
@@ -42,7 +45,6 @@
     int o = 0;
     int c = 0;
     char ch;
-    pipe->start();
     if (++o > len)                      return WAIT;
     if ('$' != pipe->next())            return NOT_FOUND;
     // this needs to be extended by crc checking 
@@ -70,7 +72,6 @@
 int GPSParser::_parseUbx(Pipe<char>* pipe, int l)
 {
     int o = 0;
-    pipe->start();
     if (++o > l)                return WAIT;
     if ('\xB5' != pipe->next()) return NOT_FOUND;   
     if (++o > l)                return WAIT;
--- a/MDM.cpp	Mon Mar 24 07:38:05 2014 +0000
+++ b/MDM.cpp	Tue Apr 08 09:15:37 2014 +0000
@@ -2,68 +2,847 @@
 #include <ctype.h>
 #include "MDM.h"
 
+#define TRACE           (1)?:printf
+//#define DEBUG           
+#define PROFILE         "0"   // this is the psd profile used
+#define MAX_SIZE        256  // max expected messages
+// some helper 
+#define ISSOCKET(s)     (((s) >= 0) && ((s) < (sizeof(_sockets)/sizeof(*_sockets))))
+#define IPSTR           "%d.%d.%d.%d"
+#define IPNUM(addr)     (addr>>24)&0xff, (addr>>16)&0xff, (addr>>8)&0xff, addr&0xff
+#define IPADR(a,b,c,d)  ((a<<24) | (b<<16) | (c<<8) | d)
+
+#ifdef DEBUG
+void dump(const char* buf, int len)
+{
+    while (len --) {
+        char ch = *buf++;
+        if      (ch == '\r') printf("\\r");
+        else if (ch == '\n') printf("\\n");
+        else if (ch >= 0x20) printf("%c", ch);
+        else                 printf("\\x%02x", ch);
+    }
+}
+#endif        
+
+MDMParser::MDMParser(void)
+{
+    _model   = MODEL_UNKNOWN;
+    _sim     = SIM_UNKNOWN;
+    _net     = NET_UNKNOWN;
+    _ip      = 0;
+    _rssi    = 0;
+    for (int socket = 0; socket < sizeof(_sockets)/sizeof(*_sockets); socket++) {
+        _sockets[socket].state = SOCK_FREE;
+        _sockets[socket].pending = 0;
+    }
+}
 
 int MDMParser::send(const char* buf, int len)
 {
+#ifdef DEBUG
+    printf("   send \"");
+    dump(buf,len);
+    printf("\"\n");
+#endif
     return _send(buf, len);
 }
 
-int MDMParser::_getLine(Pipe<char>* pipe, char* buffer, int length)
-{
-    int o = 0;
-    int i = 0;
-    int l = pipe->start();
-    while ((i < l) && (o < length))
-    {
-        int t = pipe->next();
-        i ++;
-        if (t == '\r')     // terminate commands with carriage return
+int MDMParser::sendFormated(const char* format, ...) {
+    char buf[MAX_SIZE];
+    va_list args;
+    va_start(args, format);
+    int len = vsnprintf(buf,sizeof(buf), format, args);
+    va_end(args);
+    return send(buf, len);
+}
+
+int MDMParser::waitFinalResp(_CB cb /*= NULL*/, void* param /*= NULL*/, int timeout_ms /*= 5000*/) {
+    char buf[MAX_SIZE];
+    Timer timer;
+    timer.start();
+    do {
+        int ret = getLine(buf, sizeof(buf));
+#ifdef DEBUG
+        if ((ret != WAIT) && (ret != NOT_FOUND))
+        {
+            printf("   line %06X \"", ret);
+            dump(buf, LENGTH(ret));
+            printf("\"\n");
+        }
+#endif        
+        if ((ret != WAIT) && (ret != NOT_FOUND))
         {
-            pipe->done();
-            if (length > o)
-                buffer[o] = '\0';
-            return o;          // if enter send the zero char
+            int type = TYPE(ret);
+            if (type == TYPE_OK)        return OK;
+            if (type == TYPE_ERROR)     return ERROR;
+            if (type == TYPE_PROMPT)    return PROMPT;
+            // handle unsolicited commands here
+            if (type == TYPE_PLUS) {
+                const char* cmd = buf+3;
+                int a, b, c, d;
+                char s[8];
+
+                // +CSQ: <rssi>,<qual>
+                if (sscanf(cmd, "CSQ: %d,%d",&a,&b) == 2) {
+                    if (a != 99) _rssi = -113 - 2*a;  // 0: -113 1: -111 ... 30: -53 dBm with 2 dBm steps
+                    //if (b != 99) int qual = b;  // 
+                // Socket Specific Command ---------------------------------
+                // +UUSORD: <socket>,<length>
+                } else if ((sscanf(cmd, "UUSORD: %d,%d", &a, &b) == 2) && 
+                    ISSOCKET(a) && (_sockets[a].state == SOCK_CONNECTED)) {
+                    TRACE("Socket %d: %d bytes pending\n", a, b);
+                    _sockets[a].pending = b;
+                // +UUSORF: <socket>,<length>
+                } else if ((sscanf(cmd, "UUSORF: %d,%d", &a, &b) == 2) && 
+                    ISSOCKET(a) && (_sockets[a].state == SOCK_CONNECTED)) {
+                    TRACE("Socket %d: %d bytes pending\n", a, b);
+                    _sockets[a].pending = b;
+                // +UUSOCL: <socket>
+                } else if ((sscanf(cmd, "UUSOCL: %d", &a) == 1) && 
+                    ISSOCKET(a) && (_sockets[a].state == SOCK_CONNECTED)) {
+                    TRACE("Socket %d: closed by remote host\n", a);
+                    _sockets[a].state = SOCK_CREATED/*=CLOSED*/;
+                }                
+                if (_model == MODEL_LISA_C200) {
+                    // CDMA Specific -------------------------------------------
+                    // +CREG: <n><SID>,<NID>,<stat>
+                    if (sscanf(cmd, "CREG: %*d,%*d,%*d,%d",&a) == 1) {
+                        if      (a == 0) _net = NET_NONE;     // not registered, home network
+                        else if (a == 1) _net = NET_HOME;     // registered, home network
+                        else if (a == 2) _net = NET_NONE;     // not registered, but MT is currently searching a new operator to register to
+                        else if (a == 3) _net = NET_DENIED;   // registration denied
+                        else if (a == 5) _net = NET_ROAMING;  // registered, roaming
+                    // +CSS: <mode>[,<format>,<oper>[,<AcT>]]
+                    } else if (sscanf(cmd, "CSS %*c,%2s,%*d",s) == 1) {
+                        //_net = (strcmp("Z", s) == 0) ? NET_UNKNOWN : NET_HOME;
+                    // +CMIP: xxx.xxx.xxx.xxx
+                    } else if (sscanf(cmd, "CMIP: " IPSTR, &a,&b,&c,&d) == 4) {
+                        _ip = IPADR(a,b,c,d);
+                    }
+                } else {
+                    // GSM/UMTS Specific -------------------------------------------
+                    // +CREG: <n>, <stat>[,<lac>,<ci>[,AcT]]
+                    b = 255;
+                    if (sscanf(cmd, "CREG: %*d,%d,%*d,%d",&a,&b) >= 1) {
+                        // network status
+                        if      (a == 0) _net = NET_NONE;     // 0: not registered, home network
+                        else if (a == 1) _net = NET_HOME;     // 1: registered, home network
+                        else if (a == 2) _net = NET_NONE;     // 2: not registered, but MT is currently searching a new operator to register to
+                        else if (a == 3) _net = NET_DENIED;   // 3: registration denied
+                        else if (a == 4) _net = NET_UNKNOWN;  // 4: unknown
+                        else if (a == 5) _net = NET_ROAMING;  // 5: registered, roaming
+                        // access technology
+                        if      (b == 0) ;                    // 0: GSM
+                        else if (b == 1) ;                    // 1: GSM COMPACT
+                        else if (b == 2) ;                    // 2: UTRAN
+                        else if (b == 3) ;                    // 3: GSM with EDGE availability
+                        else if (b == 4) ;                    // 4: UTRAN with HSDPA availability
+                        else if (b == 5) ;                    // 5: UTRAN with HSUPA availability
+                        else if (b == 6) ;                    // 6: UTRAN with HSDPA and HSUPA availability
+                    // +COPS: <mode>[,<format>,<oper>[,<AcT>]]
+                    } else if (sscanf(cmd, "COPS: %*d,%*d,\"%*[^\"]\",%d",&a) == 1) {
+                    //  if      (a == 0) ;                    // 0: GSM, 
+                    //  else if (a == 2) ;                    // 2: UTRAN
+                    // +CPIN: <code>
+                    } else if (sscanf(cmd, "CPIN: %8s",s) == 1) {
+                        _sim = (strcmp("READY", s) == 0) ? SIM_READY : SIM_UNKNOWN;
+                    // +UPSND=<profile_id>,<param_tag>[,<dynamic_param_val>]
+                    } else if (sscanf(cmd, "UPSND: " PROFILE ",0,\"" IPSTR "\"", &a,&b,&c,&d) == 4) {
+                        _ip = IPADR(a,b,c,d);
+                    // +UUPSDD: <profile_id> 
+                    } else if (sscanf(cmd, "UUPSDD: %d",&a) == 1) {
+                        if (*PROFILE == a) _ip = 0;
+                    }
+                }
+            }
+            if (cb) {
+                int len = LENGTH(ret);
+                int ret = cb(type, buf, len, param);
+                if (WAIT != ret)
+                    return ret; 
+            }
+        }
+        // relax a bit
+        wait_ms(10); 
+    }
+    while (timer.read_ms() < timeout_ms);
+    timer.stop();
+    timer.reset();
+    return WAIT;
+}
+
+// ----------------------------------------------------------------
+
+int MDMParser::_cbATI(int type, const char* buf, int len, Model* model)
+{
+    if ((type == TYPE_UNKNOWN) && model) {
+        if (strstr(buf, "SARA-G350")) {
+            *model = MODEL_SARA_G350;
+        } else if (strstr(buf, "LISA-U200")) {
+            *model = MODEL_LISA_U200;
+        } else if (strstr(buf, "LISA-C200")) {
+            *model= MODEL_LISA_C200;
+        }
+    }
+    return WAIT;
+}
+
+bool MDMParser::init(const char* pin)
+{
+    for(int i = 0; i < 5; i++) {
+        // check interface and disable local echo
+        sendFormated("AT\r\n");
+        if(OK == waitFinalResp())
+            break;
+    }
+    // echo off
+    sendFormated("AT E0\r\n");
+    if(OK != waitFinalResp())
+        return false;
+    // enable verbose error messages
+    sendFormated("AT+CMEE=2\r\n");
+    if(OK != waitFinalResp())
+        return false;
+    // set baud rate
+    sendFormated("AT+IPR=115200\r\n");
+    if (OK != waitFinalResp())
+        return false;
+    wait_ms(40);
+    // disable flow control
+    sendFormated("AT&K0\r\n");
+    if (OK != waitFinalResp())
+        return false;
+    // identify the module 
+    sendFormated("ATI\r\n");
+    if (OK != waitFinalResp((_CB)_cbATI, &_model))
+        return false;
+    if (_model == MODEL_UNKNOWN)
+        return false;
+    // model specific init
+    if (_model == MODEL_LISA_C200) {
+        // Return the pseudo ESN or MEID
+        sendFormated("AT+GSN\r\n");
+        if (OK != waitFinalResp())
+            return false;
+    } else {
+        // enable power saving
+        sendFormated("AT+UPSV=1\r\n");
+        if (OK != waitFinalResp())
+            return false;
+        // enable the network identification feature 
+        if (_model == MODEL_LISA_U200) {
+            sendFormated("AT+UGPIOC=20,2\r\n");
+            if (OK != waitFinalResp())
+                return false;
+        } else {
+            sendFormated("AT+UGPIOC=16,2\r\n");
+            if (OK != waitFinalResp())
+                return false;
+        }
+        // Enter PIN if needed
+        if (pin) {
+            sendFormated("AT+CPIN=%s\r\n", pin);
+            if (OK != waitFinalResp())
+                return false;
         }
-        else if (t == '\n')     // skip/filter new line 
-             /* skip */;
-        else if (t != '\b')     // normal char (no backspace)
-            buffer[o++] = t;
-        else if (o > 0)         // backspace
-            o --;               // remove it
+        // check the sim card
+        for (int i = 0; i < 5; i++) {
+            sendFormated("AT+CPIN?\r\n");
+            int ret = waitFinalResp();
+            if ((OK != ret) && (ERROR != ret))
+                return false;
+            if (_sim != SIM_UNKNOWN)
+                break;
+            wait_ms(1000);
+        }
+        if (_sim != SIM_READY)
+            return false;
+        // Returns the ICCID (Integrated Circuit Card ID) of the SIM-card. 
+        // ICCID is a serial number identifying the SIM.
+        sendFormated("AT+CCID\r\n");
+        if (OK != waitFinalResp())
+            return false;
+        // Returns the product serial number, IMEI (International Mobile Equipment Identity)
+        sendFormated("AT+CGSN\r\n");
+        if (OK != waitFinalResp())
+            return false;
+        // Setup SMS in text mode 
+        sendFormated("AT+CMGF=1\r\n");
+        if (OK != waitFinalResp())
+            return false;
+        // Configure New message indication
+        //sendFormated("AT+CNMI=2,1,0,0,0\r\n");
+        //if (OK != waitFinalResp())
+        //    return false;
+            
+    } 
+    // Request IMSI (International Mobile Subscriber Identification)
+    sendFormated("AT+CIMI\r\n");
+    if (OK != waitFinalResp())
+        return false;
+    return true; 
+}
+
+bool MDMParser::checkNetStatus(void)
+{
+    // check registration
+    sendFormated("AT+CREG?\r\n");
+    if (OK != waitFinalResp())
+        return false;
+    if ((_net != NET_ROAMING) && (_net != NET_HOME))
+        return false;
+    // check modem specific status messages 
+    if (_model == MODEL_LISA_C200) {
+        sendFormated("AT+CSS?\r\n");
+        if (OK != waitFinalResp())
+            return false;
+        if ((_net != NET_ROAMING) && (_net != NET_HOME))
+            return false;
+    } else {
+        // check operator selection 
+        sendFormated("AT+COPS?\r\n");
+        if (OK != waitFinalResp())
+            return false;
+        // Returns the MSISDNs related to this subscriber
+        sendFormated("AT+CNUM\r\n");
+        if (OK != waitFinalResp())
+            return false;
+    }  
+    // Returns the signal strength indication
+    sendFormated("AT+CSQ\r\n");
+    if (OK != waitFinalResp())
+        return false;
+    return true;
+}
+
+bool MDMParser::powerOff(void)
+{
+    sendFormated("AT+CPWROFF\r\n");
+    if (OK != waitFinalResp(NULL,NULL,120))
+        return false;
+    return true;
+}
+
+// ----------------------------------------------------------------
+// internet connection 
+
+IP MDMParser::strToIp(const char* str)
+{
+    IP ip = 0;
+    char* p = (char*)str;
+    for(int i = 0; i < 4; i++) {
+        ip |= atoi(p);
+        p = strchr(p, '.');
+        if (p == NULL) {
+            break;
+        }
+        ip <<= 8;
+        p++;
     }
-    o = 0;
-    if (length > 0)
-        buffer[0] = '\0';
+    return ip;
+}
+
+bool MDMParser::join(const char* apn, const char* user /*= NULL*/, const char* password /*= NULL*/)
+{
+    if (_model == MODEL_LISA_C200) {
+#ifdef TODO // TODO implement 
+        // enable the 
+        sendFormated("AT$QCMIPEP=1\r\n");
+        if (OK != waitFinalResp())
+            return false;
+        //Get local IP address
+        sendFormated("AT+CMIP?\r\n");
+        if (OK != waitFinalResp())
+            return false;
+#endif
+    } else { 
+        // check gprs attach status 
+        sendFormated("AT+CGATT?\r\n");
+        if (OK != waitFinalResp())
+            return false;
+         // Set up the APN
+        if (apn) {
+            sendFormated("AT+UPSD=" PROFILE ",1,\"%s\"\r\n", apn);
+            if (OK != waitFinalResp())
+                return false;
+        }
+        if (user) {    
+            sendFormated("AT+UPSD=" PROFILE ",2,\"%s\"\r\n", user);
+            if (OK != waitFinalResp())
+                return false;
+        }
+        if (password) {
+            sendFormated("AT+UPSD=" PROFILE ",3,\"%s\"\r\n", password);
+            if (OK != waitFinalResp())
+                return false;
+        }
+        // Set up the dynamic IP address assignment.
+        sendFormated("AT+UPSD=" PROFILE ",7,\"0.0.0.0\"\r\n");
+        if (OK != waitFinalResp())
+            return false;
+        // Activate the profile and make connection
+        sendFormated("AT+UPSDA=" PROFILE ",3\r\n");
+        if (OK != waitFinalResp())
+            return false;
+        //Get local IP address
+        sendFormated("AT+UPSND=" PROFILE ",0\r\n");
+        if (OK != waitFinalResp())
+            return false;
+    }
+    if (!_ip)
+        return false;
+    printf("Got IP address: " IPSTR "\n",  IPNUM(_ip));
+    return true;
+}
+
+bool MDMParser::disconnect(void)
+{
+    if (_ip == 0)
+        return true;
+    if (_model == MODEL_LISA_C200) {
+#ifdef TODO // TODO implement 
+        sendFormated("AT$QCMIPEP=0\r\n");
+#endif
+    } else { 
+        sendFormated("AT+UPSDA=" PROFILE ",4\r\n");
+    }
+    if (OK != waitFinalResp())
+        return false;
+    _ip = 0;
+    return true;
+}
+
+int MDMParser::_cbUDNSRN(int type, const char* buf, int len, IP* ip)
+{
+    if ((type == TYPE_PLUS) && ip) {
+        buf += 3;
+        int a,b,c,d;
+        if (sscanf(buf, "UDNSRN: \""IPSTR"\"", &a,&b,&c,&d) == 4)
+            *ip = IPADR(a,b,c,d);
+    }
     return WAIT;
 }
 
-int MDMParser::_getResp(Pipe<char>* pipe, char* buffer, int length)
+bool MDMParser::gethostbyname(const char* host, IP* ip)
+{
+    char ipstr[16];
+    IP addr = strToIp(host);
+    *ip = 0;
+    snprintf(ipstr, sizeof(ipstr), IPSTR, IPNUM(addr));
+    if (strcmp(ipstr, host) == 0) {
+        *ip = addr;
+        return true;
+    }
+    sendFormated("AT+UDNSRN=0,\"%s\"\r\n", host);
+    if (OK != waitFinalResp((_CB)_cbUDNSRN, ip))
+        return false;
+    return *ip != 0;
+}
+
+// ----------------------------------------------------------------
+// sockets
+
+int MDMParser::_cbUSOCR(int type, const char* buf, int len, int* socket)
+{
+    if ((type == TYPE_PLUS) && socket) {
+        const char* p = strstr(buf,"+USOCR: "); 
+        if (p)
+            *socket = atoi(p+8);
+    }
+    return WAIT;
+}
+
+int MDMParser::socketSocket(IpProtocol ipproto)
+{
+    const char* cmd;
+    if(ipproto == IPPROTO_TCP) {
+        cmd = "AT+USOCR=6\r\n";
+    } else if(ipproto == IPPROTO_UDP) {
+        cmd = "AT+USOCR=17\r\n";
+    } else { // other types not supported
+        return SOCKET_ERROR;
+    }
+    sendFormated(cmd);
+    int socket = -1;
+    if (OK != waitFinalResp((_CB)_cbUSOCR, &socket))
+        return SOCKET_ERROR;
+    if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_FREE))
+        return SOCKET_ERROR;
+    // successfull
+    _sockets[socket].state = SOCK_CREATED;
+    _sockets[socket].pending = 0;
+    return socket;
+}
+
+bool MDMParser::socketConnect(int socket, const char * host, int port)
+{
+    IP ip;
+    if (!gethostbyname(host, &ip))
+        return false;
+    // connect to socket
+    if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_CREATED))
+        return false;
+    sendFormated("AT+USOCO=%d,\"" IPSTR "\",%d\r\n", socket, IPNUM(ip), port);
+    if (OK != waitFinalResp())
+        return false;
+    _sockets[socket].state = SOCK_CONNECTED;
+    return true;
+}
+
+bool  MDMParser::socketClose(int socket)
+{
+    if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_CONNECTED))
+        return false;
+    sendFormated("AT+USOCL=%d\r\n", socket);
+    if (OK != waitFinalResp())
+        return false;
+    return true;
+}
+
+bool  MDMParser::socketFree(int socket)
+{
+    socketClose(socket);
+    if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_CREATED))
+        return false;
+    _sockets[socket].state = SOCK_FREE;
+    return true;
+}
+
+int MDMParser::socketSend(int socket, const char * buf, int len)
+{
+    if(len > 0) {
+        sendFormated("AT+USOWR=%d,%d\r\n",socket,len);
+        if (PROMPT != waitFinalResp())
+            return SOCKET_ERROR;
+        wait_ms(50);
+        send(buf, len);
+        if (OK != waitFinalResp()) 
+            return SOCKET_ERROR;
+    }
+    return len;
+}
+
+int MDMParser::socketSendTo(int socket, IP ip, int port, const char * buf, int len)
+{
+    if(len > 0) {
+        sendFormated("AT+USOWR=%d,\"" IPSTR "\",%d,%d\r\n",socket,IPNUM(ip),port,len);
+        if (PROMPT != waitFinalResp())
+            return SOCKET_ERROR;
+        wait_ms(50);
+        send(buf, len);
+        if (OK != waitFinalResp()) 
+            return SOCKET_ERROR;
+    }
+    return len;
+}
+
+int MDMParser::socketReadable(int socket)
+{
+    if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_CONNECTED))
+        return SOCKET_ERROR;
+    // allow to receive unsolicited commands 
+    waitFinalResp(NULL, NULL, 0);
+    if (_sockets[socket].state != SOCK_CONNECTED)
+        return SOCKET_ERROR;
+    return _sockets[socket].pending;
+}
+
+int MDMParser::_cbUSORD(int type, const char* buf, int len, char* out)
+{
+    if ((type == TYPE_PLUS) && out) {
+        int sz, sk;
+        if ((sscanf(buf, "\r\n+USORD: %d,%d,", &sk, &sz) == 2) && 
+            (buf[len-sz-2] == '\"') && (buf[len-1] == '\"')) {
+            memcpy(out, &buf[len-1-sz], sz);
+        }
+    }
+    return WAIT;
+}
+
+int MDMParser::socketRecv(int socket, char* buf, int len)
+{
+    int cnt = 0;
+    if (!ISSOCKET(socket))
+        return SOCKET_ERROR;
+    memset(buf, '\0', len);
+    while (len) {
+        int blk = MAX_SIZE - 64; // still need space for headers and unsolicited  commands 
+        if (_sockets[socket].state != SOCK_CONNECTED)
+            return cnt ? cnt : SOCKET_ERROR;
+        if (_sockets[socket].pending < blk)
+            blk = _sockets[socket].pending;
+        if (len < blk) blk = len;
+        if (blk) {
+             sendFormated("AT+USORD=%d,%d\r\n",socket, blk);
+            if (OK != waitFinalResp((_CB)_cbUSORD, buf)) {
+                return cnt ? cnt : SOCKET_ERROR;
+            }
+            len -= blk;
+            cnt += blk;
+            buf += blk;
+            _sockets[socket].pending -= blk;
+        } else {
+            // allow to receive unsolicited commands 
+            waitFinalResp(NULL, NULL, 10);
+        }
+    }
+    return cnt;
+}
+
+int MDMParser::_cbUSORF(int type, const char* buf, int len, USORFparam* param)
+{
+    if ((type == TYPE_PLUS) && param) {
+        int sz, sk, p, a,b,c,d;
+        if ((sscanf(buf, "\r\n+USORF: %d,\""IPSTR"\",%d,%d,", 
+            &sk,&a,&b,&c,&d,&p,&sz) == 7) && 
+            (buf[len-sz-2] == '\"') && (buf[len-1] == '\"')) {
+            memcpy(param->buf, &buf[len-1-sz], sz);
+            param->ip = IPADR(a,b,c,d);
+            param->port = p;
+        }
+    }
+    return WAIT;
+}
+
+int MDMParser::socketRecvFrom(int socket, char* buf, int len, IP* ip)
+{
+    int cnt = 0;
+    if (!ISSOCKET(socket))
+        return SOCKET_ERROR;
+    memset(buf, '\0', len);
+    while (len) {
+        int blk = MAX_SIZE - 64; // still need space for headers and unsolicited commands 
+        if (_sockets[socket].state != SOCK_CONNECTED)
+            return cnt ? cnt : SOCKET_ERROR;
+        if (_sockets[socket].pending < blk)
+            blk = _sockets[socket].pending;
+        if (len < blk) blk = len;
+        if (blk) {
+            sendFormated("AT+USORF=%d,%d\r\n",socket, blk);
+            USORFparam param;
+            param.buf = buf;
+            if (OK != waitFinalResp((_CB)_cbUSORF, &param)) {
+                return cnt ? cnt : SOCKET_ERROR;
+            }
+            *ip = param.ip;
+            //*port = param.port;
+            len -= blk;
+            cnt += blk;
+            buf += blk;
+            _sockets[socket].pending -= blk;
+        } else {
+            // allow to receive unsolicited commands 
+            waitFinalResp(NULL, NULL, 10);
+        }
+    }
+    return cnt;
+}
+
+// ----------------------------------------------------------------
+#if 0
+void _cbCMGL()
+{ 
+    //
+}
+
+int MDMParser::smsCount(void)
+{
+    int num = 0;
+    sendFormated("AT+CMGL=ALL\r\n");
+    if (OK != waitFinalResp((_CB)_cbCMGL,&num)) {
+        return false;
+    }
+    return true;
+}
+#endif
+
+bool MDMParser::smsSend(const char* num, const char* buf)
+{
+    sendFormated("AT+CMGS=\"%s\"\r",num);
+    if (PROMPT != waitFinalResp()) {
+        return false;
+    }
+    send(buf, strlen(buf));
+    const char ctrlZ = 0x1A;
+    send(&ctrlZ, sizeof(ctrlZ));
+    if (OK != waitFinalResp()) {
+        return false;
+    }
+    return true;
+}
+
+bool MDMParser::smsDelete(int ix)
+{
+    sendFormated("AT+CMGD=%d\r\n",ix);
+    if (OK != waitFinalResp()) {
+        return false;
+    }
+    return true;
+}
+
+int MDMParser::_cbCMGR(int type, const char* buf, int len, CMGRparam* param)
+{
+    if (param) {
+        if (type == TYPE_PLUS) {
+            if (sscanf(buf, "\r\n+CMGR: \"%*[^\"]\",\"%[^\"]", param->num) == 1) {
+            }
+        } else if ((type == TYPE_UNKNOWN) && (buf[len-2] == '\r') && (buf[len-1] == '\n')) {
+            memcpy(param->buf, buf, len-2);
+            param->buf[len-2] = '\0';
+        }
+    }
+    return WAIT;
+}
+
+bool MDMParser::smsRead(int ix, char* num, char* buf, int len)
+{
+    CMGRparam param;
+    param.num = num;
+    param.buf = buf;
+    sendFormated("AT+CMGR=%d\r\n",ix);
+    if (OK != waitFinalResp((_CB)_cbCMGR, &param)) {
+        return false;
+    }
+    return true;
+}
+    
+// ----------------------------------------------------------------
+  
+int MDMParser::_cbCUSD(int type, const char* buf, int len, char* resp)
+{
+    if ((type == TYPE_PLUS) && resp) {
+        // +USD: \"%*[^\"]\",\"%[^\"]\",,\"%*[^\"]\",%d,%d,%d,%d,\"*[^\"]\",%d,%d"..);
+        if (sscanf(buf, "\r\n+CUSD: %*d,\"%[^\"]\",%*d", resp) == 1) {
+            /*nothing*/            
+        }
+    }
+    return WAIT;
+}  
+int MDMParser::ussdCommand(const char* cmd, char* buf, int len)
+{
+    *buf = '\0';
+    sendFormated("AT+CUSD=1,\"%s\"\r\n",cmd);
+    if (OK != waitFinalResp((_CB)_cbCUSD, buf)) {
+        return -1;
+    }
+    return strlen(buf);
+}
+    
+   
+// ----------------------------------------------------------------
+int MDMParser::_parseMatch(Pipe<char>* pipe, int len, const char* sta, const char* end)
 {
     int o = 0;
-    int i = 0;
-    int l = pipe->start();
-    static const char erTxt[] = "ERROR\r\n";
-    static const char okTxt[] = "OK\r\n";
-    int er = 0;
-    int ok = 0;
-    while ((i < pipe->size()) && (o < length))
-    {
-        int t = pipe->next();
-        i ++;
-        buffer[o++] = t;
-        ok = (t == okTxt[ok]) ? ok + 1 : 0;
-        er = (t == erTxt[er]) ? er + 1 : 0;
-        if ((okTxt[ok] == '\0') || (erTxt[er] == '\0'))
-        {
-            pipe->done();
-            if (length > o)
-                buffer[o] = '\0';
-            return o;
+    if (sta) {
+        while (*sta) {
+            if (++o > len)                  return WAIT;
+            char ch = pipe->next();
+            if (*sta++ != ch)               return NOT_FOUND;
+        }
+    }
+    if (!end)                               return o; // no termination
+    int x = 0;
+    while (end[x]) {
+        if (++o > len)                      return WAIT;
+        char ch = pipe->next();
+        x = (end[x] == ch) ? x + 1 : 
+            (end[0] == ch) ? 1 : 
+                            0;
+    }
+    return o;
+}
+
+int MDMParser::_parseFormated(Pipe<char>* pipe, int len, const char* fmt)
+{
+    int o = 0;
+    int num = 0;
+    if (fmt) {
+        while (*fmt) {
+            if (++o > len)                  return WAIT;
+            char ch = pipe->next();
+            if (*fmt == '%') {
+                fmt++;
+                if (*fmt == 'd') { // numeric
+                    fmt ++;
+                    num = 0;
+                    while (ch >= '0' && ch <= '9') {
+                        num = num * 10 + (ch - '0'); 
+                        if (++o > len)      return WAIT;
+                        ch = pipe->next();
+                    }
+                }   
+                else if (*fmt == 'c') { // char buffer (takes last numeric as length)
+                    fmt ++;
+                    while (num --) {
+                        if (++o > len)      return WAIT;
+                        ch = pipe->next();
+                    }   
+                }
+            }
+            if (*fmt++ != ch)               return NOT_FOUND;
         }
     }
-    o = 0;
-    if (length > 0)
-        buffer[0] = '\0';
+    return o; 
+}
+
+
+int MDMParser::_getLine(Pipe<char>* pipe, char* buf, int len)
+{
+    int unkn = 0;
+    int sz = pipe->size();
+    int fr = pipe->free();
+    if (len > sz)
+        len = sz;
+    while (len > 0)
+    {
+        static struct { 
+              const char* fmt;                              int type; 
+        } lutF[] = {
+            { "\r\n+USORD: %d,%d,\"%c\"",                   TYPE_PLUS       },
+            { "\r\n+USORF: %d,\""IPSTR"\",%d,%d,\"%c\"",    TYPE_PLUS       },
+        };
+        static struct { 
+              const char* sta;          const char* end;    int type; 
+        } lut[] = {
+            { "\r\nOK\r\n",             NULL,               TYPE_OK         },
+            { "\r\nERROR\r\n",          NULL,               TYPE_ERROR      },
+            { "\r\n+CME ERROR:",        "\r\n",             TYPE_ERROR      },
+            { "\r\n+CMS ERROR:",        "\r\n",             TYPE_ERROR      },
+            { "\r\nRING\r\n",           NULL,               TYPE_RING       },
+            { "\r\nCONNECT\r\n",        NULL,               TYPE_CONNECT    },
+            { "\r\nNO CARRIER\r\n",     NULL,               TYPE_NOCARRIER  },
+            { "\r\nNO DIALTONE\r\n",    NULL,               TYPE_NODIALTONE },
+            { "\r\nBUSY\r\n",           NULL,               TYPE_BUSY       },
+            { "\r\nNO ANSWER\r\n",      NULL,               TYPE_NOANSWER   },
+            { "\r\n+",                  "\r\n",             TYPE_PLUS       },
+            { "\r\n@",                  NULL,               TYPE_PROMPT     }, // Sockets
+            { "\r\n>",                  NULL,               TYPE_PROMPT     }, // SMS
+        };
+        for (int i = 0; i < sizeof(lutF)/sizeof(*lutF); i ++) {
+            pipe->set(unkn);
+            int ln = _parseFormated(pipe, len, lutF[i].fmt);
+            if (ln == WAIT && fr)                       
+                return WAIT;
+            if ((ln != NOT_FOUND) && (unkn > 0))  
+                return pipe->get(buf, unkn);
+            if (ln > 0)
+                return lutF[i].type  | pipe->get(buf, ln);
+        }
+        for (int i = 0; i < sizeof(lut)/sizeof(*lut); i ++) {
+            pipe->set(unkn);
+            int ln = _parseMatch(pipe, len, lut[i].sta, lut[i].end);
+            if (ln == WAIT && fr)                       
+                return WAIT;
+            if ((ln != NOT_FOUND) && (unkn > 0))  
+                return pipe->get(buf, unkn);
+            if (ln > 0)
+                return lut[i].type | pipe->get(buf, ln);
+        }
+        // UNKNOWN
+        unkn ++;
+        len--;
+    }
     return WAIT;
 }
 
@@ -96,11 +875,6 @@
     return _getLine(&_pipeRx, buffer, length);
 }
 
-int MDMSerial::getResp(char* buffer, int length)
-{
-    return _getResp(&_pipeRx, buffer, length);
-}
-
 // ----------------------------------------------------------------
 // USB Implementation 
 // ----------------------------------------------------------------
@@ -110,5 +884,4 @@
 MDMUsb::MDMUsb(void)                             { }
 int MDMUsb::_send(const void* buf, int len)      { return len; }
 int MDMUsb::getLine(char* buffer, int length)    { return NOT_FOUND; }
-int MDMUsb::getResp(char* buffer, int length)    { return NOT_FOUND; }
 #endif
\ No newline at end of file
--- a/MDM.h	Mon Mar 24 07:38:05 2014 +0000
+++ b/MDM.h	Tue Apr 08 09:15:37 2014 +0000
@@ -1,6 +1,8 @@
 #pragma once 
 
 #include "mbed.h"
+#include <stdarg.h>
+
 #include "Pipe.h"
 #include "SerialPipe.h"
 
@@ -13,22 +15,97 @@
  #define _C027DEFAULT(name)
 #endif
 
+typedef uint32_t IP;
+    
 class MDMParser
 {
 public:
-    #define WAIT      -1
-    #define NOT_FOUND  0
+    // waitFinalResp Responses
+    #define NOT_FOUND    0
+    #define WAIT        -1 // TIMEOUT
+    #define OK          -2 
+    #define ERROR       -3
+    #define PROMPT      -4
+    // getLine Responses
+    #define LENGTH(x)  (x & 0x00FFFF)
+    #define TYPE(x)    (x & 0xFF0000)
+    #define TYPE_UNKNOWN    0x000000
+    #define TYPE_OK         0x110000
+    #define TYPE_ERROR      0x120000
+    #define TYPE_RING       0x210000
+    #define TYPE_CONNECT    0x220000
+    #define TYPE_NOCARRIER  0x230000
+    #define TYPE_NODIALTONE 0x240000
+    #define TYPE_BUSY       0x250000
+    #define TYPE_NOANSWER   0x260000
+    #define TYPE_PROMPT     0x300000
+    #define TYPE_PLUS       0x400000
+    // Socket Return Codes
+    #define SOCKET_ERROR -1
+    #define SOCKET_OK     0 
+    
+    MDMParser(void);
     
-    #define LENGTH(x)   (x & 0x00FFFF)
-//    #define PROTOCOL(x) (x & 0xFF0000)
+    // interaction with AT command interface
     virtual int getLine(char* buf, int len) = 0; 
-    virtual int getResp(char* buf, int len) = 0; 
     virtual int send(const char* buf, int len);
-    
+    int sendFormated(const char* format, ...);
+    typedef int (*_CB)(int type, const char* buf, int len, void* param);
+    int waitFinalResp(_CB cb = NULL, void* param = NULL, int timeout_ms = 5000);
+
+    // network
+    bool init(const char* pin = NULL);
+    bool checkNetStatus(void);
+    bool powerOff(void);
+    // internet connection
+    bool join(const char* apn, const char* user = NULL, const char* password = NULL);
+    bool disconnect(void);
+    bool gethostbyname(const char* host, IP* ip);
+    // sockets
+    typedef enum { IPPROTO_TCP, IPPROTO_UDP } IpProtocol;
+    int socketSocket(IpProtocol ipproto);
+    bool socketConnect(int socket, const char * host, int port);
+    int socketSend(int socket, const char * buf, int len);
+    int socketSendTo(int socket, IP ip, int port, const char * buf, int len);
+    int socketReadable(int socket);
+    int socketRecv(int socket, char* buf, int len);
+    int socketRecvFrom(int socket, char* buf, int len, IP* ip);
+    bool socketClose(int socket);
+    bool socketFree(int socket);
+    // sms
+    bool smsSend(const char* num, const char* buf);
+    bool smsDelete(int ix);
+    bool smsRead(int ix, char* num, char* buf, int len);
+    // ussd
+    int ussdCommand(const char* cmd, char* buf, int len);
 protected:
     static int _getLine(Pipe<char>* pipe, char* buffer, int length);
-    static int _getResp(Pipe<char>* pipe, char* buffer, int length);
+    static int _parseMatch(Pipe<char>* pipe, int len, const char* sta, const char* end);
+    static int _parseFormated(Pipe<char>* pipe, int len, const char* fmt);
     virtual int _send(const void* buf, int len) = 0;
+private:
+    typedef enum { MODEL_UNKNOWN, MODEL_SARA_G350, MODEL_LISA_U200, MODEL_LISA_C200 } Model; 
+    typedef enum { SIM_UNKNOWN, SIM_PIN, SIM_READY } Sim; 
+    typedef enum { NET_UNKNOWN, NET_DENIED, NET_NONE, NET_HOME, NET_ROAMING } Net; 
+    static int _cbATI(int type, const char* buf, int len, Model* model);
+    static int _cbUDNSRN(int type, const char* buf, int len, IP* ip);
+    static int _cbUSOCR(int type, const char* buf, int len, int* socket);
+    static int _cbUSORD(int type, const char* buf, int len, char* out);
+    typedef struct { char* buf; IP ip; int port; } USORFparam;
+    static int _cbUSORF(int type, const char* buf, int len, USORFparam* param);
+    typedef struct { char* buf; char* num; } CMGRparam;
+    static int _cbCUSD(int type, const char* buf, int len, char* buf);
+    static int _cbCMGR(int type, const char* buf, int len, CMGRparam* param);
+    static IP strToIp(const char* str);
+    IP _ip;
+    Model _model;
+    Sim _sim;
+    Net _net;
+    int _rssi;
+private:
+    typedef enum { SOCK_FREE, SOCK_CREATED, SOCK_CONNECTED } SockState;
+    typedef struct { SockState state; int pending; } SockCtrl;
+    SockCtrl _sockets[16];    
 };
 
 // -----------------------------------------------------------------------
@@ -44,9 +121,8 @@
               PinName cts   _C027DEFAULT(MDMCTS),
 #endif
               int rxSize    = 256 , 
-              int txSize    = 256 );
+              int txSize    = 128 );
     virtual int getLine(char* buffer, int length);
-    virtual int getResp(char* buffer, int length);
 protected:
     virtual int _send(const void* buf, int len);
 };
@@ -60,7 +136,6 @@
 public: 
     MDMUsb(void);
     virtual int getLine(char* buffer, int length);
-    virtual int getResp(char* buffer, int length);
 protected:
     virtual int _send(const void* buf, int len);
 };
--- a/Pipe.h	Mon Mar 24 07:38:05 2014 +0000
+++ b/Pipe.h	Tue Apr 08 09:15:37 2014 +0000
@@ -25,6 +25,17 @@
         if (_a) 
             delete [] _a;
     }
+    void dump(void)
+    {
+        int o = _r;
+        printf("pipe: %d/%d ", size(), _s);
+        while (o != _w) {
+            T t = _b[o]; 
+            printf("%0*X", sizeof(T)*2, t);
+            o = _inc(o); 
+        }
+        printf("\n");
+    }
     // writing thread
     bool writeable(void) // = not full
     {
@@ -128,10 +139,12 @@
     // the following functions are useful if you like to inspect or parse the buffer
     
     //! reset the parsing index and return the number of available elments 
-    virtual int start(void) 
+    virtual int set(int ix) 
     {
-        _o = _r; 
-        return size(); 
+        int sz = size();
+        ix = (ix > sz) ? sz : ix;
+        _o = _inc(_r, ix); 
+        return sz - ix;
     }
     //! get the next element and increment 
     virtual T next(void)