C027_Support library plus AT Comand for dialing.
Fork of C027_Support_New by
Revision 31:a0bed6c1e05d, committed 2014-04-09
- Comitter:
- mazgch
- Date:
- Wed Apr 09 11:48:04 2014 +0000
- Parent:
- 30:1a647403171b
- Child:
- 32:8f12ac182bbb
- Commit message:
- restructure the modem APIs make them more robust
Changed in this revision
--- a/GPS.cpp Tue Apr 08 15:49:04 2014 +0000 +++ b/GPS.cpp Wed Apr 09 11:48:04 2014 +0000 @@ -15,7 +15,7 @@ pipe->set(unkn); int nmea = _parseNmea(pipe,len); if ((nmea != NOT_FOUND) && (unkn > 0)) - return pipe->get(buf,unkn); + return UNKNOWN | pipe->get(buf,unkn); if (nmea == WAIT && fr) return WAIT; if (nmea > 0) @@ -25,7 +25,7 @@ pipe->set(unkn); int ubx = _parseUbx(pipe,len); if ((ubx != NOT_FOUND) && (unkn > 0)) - return pipe->get(buf,unkn); + return UNKNOWN | pipe->get(buf,unkn); if (ubx == WAIT && fr) return WAIT; if (ubx > 0) @@ -36,7 +36,7 @@ len--; } if (unkn > 0) - return pipe->get(buf,unkn); + return UNKNOWN | pipe->get(buf,unkn); return WAIT; } @@ -104,6 +104,13 @@ return _send(buf, len); } +void GPSParser::powerOff(void) +{ + // set the gps into backup mode using the command RMX-LPREQ + struct { unsigned long dur; unsigned long flags; } msg = {0/*endless*/,0/*backup*/}; + sendUbx(0x02, 0x41, &msg, sizeof(msg)); +} + int GPSParser::sendNmea(const char* buf, int len) { char head[1] = { '$' };
--- a/GPS.h Tue Apr 08 15:49:04 2014 +0000 +++ b/GPS.h Wed Apr 09 11:48:04 2014 +0000 @@ -19,6 +19,7 @@ #define WAIT -1 #define NOT_FOUND 0 + #define UNKNOWN 0x000000 #define UBX 0x100000 #define NMEA 0x200000 #define LENGTH(x) (x & 0x00FFFF) @@ -28,6 +29,7 @@ virtual int send(const char* buf, int len); virtual int sendNmea(const char* buf, int len); virtual int sendUbx(unsigned char cls, unsigned char id, const void* buf = NULL, int len = 0); + void powerOff(void); static const char* findNmeaItemPos(int ix, const char* start, const char* end); static bool getNmeaItem(int ix, char* buf, int len, double& val);
--- a/MDM.cpp Tue Apr 08 15:49:04 2014 +0000 +++ b/MDM.cpp Wed Apr 09 11:48:04 2014 +0000 @@ -1,5 +1,6 @@ #include "mbed.h" #include <ctype.h> +#include <string.h> #include "MDM.h" #define TRACE (0)?:printf @@ -8,9 +9,6 @@ #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) @@ -23,36 +21,35 @@ else printf("\\x%02x", ch); } } -#endif + #if 1 // colored terminal output using ANSI escape sequences + #define COL(c,t) "\33[" c t "\33[" "39m" + #else + #define COL(c,t) t + #endif + #define BLA(t) COL("30m",t) + #define RED(t) COL("31m",t) + #define GRE(t) COL("32m",t) + #define YEL(t) COL("33m",t) + #define BLU(t) COL("34m",t) + #define MAG(t) COL("35m",t) + #define CYA(t) COL("36m",t) + #define WHY(t) COL("37m",t) +#endif MDMParser::MDMParser(void) { - // device info - _model = MODEL_UNKNOWN; - _sim = SIM_UNKNOWN; - *_ccid = '\0'; - *_imsi = '\0'; - *_imei = '\0'; - // network info - _net = NET_UNKNOWN; - _act = ACT_UNKNOWN; - _rssi = 0; - *_num = '\0'; - *_opr = '\0'; - // data network info - _ip = 0; - for (int socket = 0; socket < sizeof(_sockets)/sizeof(*_sockets); socket++) { - _sockets[socket].state = SOCK_FREE; - _sockets[socket].pending = 0; - } + memset(&_dev, 0, sizeof(_dev)); + memset(&_net, 0, sizeof(_net)); + _ip = NOIP; + memset(_sockets, 0, sizeof(_sockets)); } int MDMParser::send(const char* buf, int len) { #ifdef DEBUG - printf(" send \""); + printf("AT send %4d \"", len); dump(buf,len); - printf("\"\n"); + printf("\"\r\n"); #endif return _send(buf, len); } @@ -78,9 +75,17 @@ #ifdef DEBUG if ((ret != WAIT) && (ret != NOT_FOUND)) { - printf(" line %06X \"", ret); - dump(buf, LENGTH(ret)); - printf("\"\n"); + int len = LENGTH(ret); + int type = TYPE(ret); + const char* s = (type == TYPE_UNKNOWN)? YEL("UNK") : + (type == TYPE_OK ) ? GRE("OK ") : + (type == TYPE_ERROR) ? RED("ERR") : + (type == TYPE_PLUS) ? CYA(" + ") : + (type == TYPE_PROMPT) ? BLU(" > ") : + "..." ; + printf("AT read %s %3d \"", s, len); + dump(buf, len); + printf("\"\r\n"); } #endif if ((ret != WAIT) && (ret != NOT_FOUND)) @@ -92,46 +97,43 @@ // handle unsolicited commands here if (type == TYPE_PLUS) { const char* cmd = buf+3; - int a, b, c, d; + int a, b; char s[32]; - // +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; // + // SMS Command --------------------------------- + // +CNMI: <mem>,<index> + if (sscanf(cmd, "CMTI: \"%*[^\"]\",%d", &a) == 1) { + TRACE("New SMS at index %d\r\n", a); // 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); + TRACE("Socket %d: %d bytes pending\r\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); + TRACE("Socket %d: %d bytes pending\r\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); + TRACE("Socket %d: closed by remote host\r\n", a); _sockets[a].state = SOCK_CREATED/*=CLOSED*/; } - if (_model == MODEL_LISA_C200) { + if (_dev.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 - _act = ACT_CDMA; + if (a == 0) _net.reg = REG_NONE; // not registered, home network + else if (a == 1) _net.reg = REG_HOME; // registered, home network + else if (a == 2) _net.reg = REG_NONE; // not registered, but MT is currently searching a new operator to register to + else if (a == 3) _net.reg = REG_DENIED; // registration denied + else if (a == 5) _net.reg = REG_ROAMING; // registered, roaming + _net.act = ACT_CDMA; // +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); + //_net.reg = (strcmp("Z", s) == 0) ? REG_UNKNOWN : REG_HOME; } } else { // GSM/UMTS Specific ------------------------------------------- @@ -139,37 +141,23 @@ 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 + if (a == 0) _net.reg = REG_NONE; // 0: not registered, home network + else if (a == 1) _net.reg = REG_HOME; // 1: registered, home network + else if (a == 2) _net.reg = REG_NONE; // 2: not registered, but MT is currently searching a new operator to register to + else if (a == 3) _net.reg = REG_DENIED; // 3: registration denied + else if (a == 4) _net.reg = REG_UNKNOWN; // 4: unknown + else if (a == 5) _net.reg = REG_ROAMING; // 5: registered, roaming // access technology - if (b == 0) _act = ACT_GSM; // 0: GSM - else if (b == 1) _act = ACT_GSM; // 1: GSM COMPACT - else if (b == 2) _act = ACT_UTRAN; // 2: UTRAN - else if (b == 3) _act = ACT_EDGE; // 3: GSM with EDGE availability - else if (b == 4) _act = ACT_UTRAN; // 4: UTRAN with HSDPA availability - else if (b == 5) _act = ACT_UTRAN; // 5: UTRAN with HSUPA availability - else if (b == 6) _act = ACT_UTRAN; // 6: UTRAN with HSDPA and HSUPA availability - // +COPS: <mode>[,<format>,<oper>[,<AcT>]] - } else if (sscanf(cmd, "COPS: %*d,%*d,\"%[^\"]\",%d",s,&b) >= 1) { - strcpy(_opr,s); - if (a == 0) _act = ACT_GSM; // 0: GSM, - else if (a == 2) _act = ACT_UTRAN; // 2: UTRAN - // +CPIN: <code> - } else if (sscanf(cmd, "CPIN: %7s",s) == 1) { - _sim = (strcmp("READY", s) == 0) ? SIM_READY : SIM_UNKNOWN; - // +CNUM: <code> - } else if (sscanf(cmd, "CNUM: \"My Number\",\"%31[^\"]\",%d", s, &a) == 2) { - if ((a == 129) || (a == 145)) strncpy(_num, s, sizeof(_num)); - // +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); + if (b == 0) _net.act = ACT_GSM; // 0: GSM + else if (b == 1) _net.act = ACT_GSM; // 1: GSM COMPACT + else if (b == 2) _net.act = ACT_UTRAN; // 2: UTRAN + else if (b == 3) _net.act = ACT_EDGE; // 3: GSM with EDGE availability + else if (b == 4) _net.act = ACT_UTRAN; // 4: UTRAN with HSDPA availability + else if (b == 5) _net.act = ACT_UTRAN; // 5: UTRAN with HSUPA availability + else if (b == 6) _net.act = ACT_UTRAN; // 6: UTRAN with HSDPA and HSUPA availability // +UUPSDD: <profile_id> } else if (sscanf(cmd, "UUPSDD: %d",&a) == 1) { - if (*PROFILE == a) _ip = 0; + if (*PROFILE == a) _ip = NOIP; } } } @@ -189,25 +177,26 @@ return WAIT; } -// ---------------------------------------------------------------- - -int MDMParser::_cbATI(int type, const char* buf, int len, Model* model) +int MDMParser::_cbString(int type, const char* buf, int len, char* str) { - if ((type == TYPE_UNKNOWN) && model) { - if (strstr(buf, "SARA-G350")) { - *model = MODEL_SARA_G350; - /*TRACE("Identified Model: SARA-G350 2G\n")*/; - } else if (strstr(buf, "LISA-U200")) { - *model = MODEL_LISA_U200; - /*TRACE("Identified Model: LISA-U200 2G/3G\n")*/; - } else if (strstr(buf, "LISA-C200")) { - *model= MODEL_LISA_C200; - /*TRACE("Identified Model: LISA-C200 CDMA\n")*/; - } + if (str && (type == TYPE_UNKNOWN)) { + if (sscanf(buf, "\r\n%s\r\n", str) == 1) + /*nothing*/; } return WAIT; } +int MDMParser::_cbInt(int type, const char* buf, int len, int* val) +{ + if (val && (type == TYPE_UNKNOWN)) { + if (sscanf(buf, "\r\n%d\r\n", val) == 1) + /*nothing*/; + } + return WAIT; +} + +// ---------------------------------------------------------------- + bool MDMParser::init(const char* pin, DevStatus* status) { for(int i = 0; i < 5; i++) { @@ -231,19 +220,19 @@ wait_ms(40); // identify the module sendFormated("ATI\r\n"); - if (OK != waitFinalResp(_cbATI, &_model)) + if (OK != waitFinalResp(_cbATI, &_dev.model)) return false; - if (_model == MODEL_UNKNOWN) + if (_dev.model == MODEL_UNKNOWN) return false; // model specific init - if (_model == MODEL_LISA_C200) { + if (_dev.model == MODEL_LISA_C200) { // disable flow control sendFormated("AT+IFC=0,0\r\n"); if (OK != waitFinalResp()) return false; // Return the pseudo ESN or MEID sendFormated("AT+GSN\r\n"); - if (OK != waitFinalResp(_cbGSN, _imei)) + if (OK != waitFinalResp(_cbString, _dev.imei)) return false; } else { // disable flow control @@ -255,7 +244,7 @@ if (OK != waitFinalResp()) return false; // enable the network identification feature - if (_model == MODEL_LISA_U200) { + if (_dev.model == MODEL_LISA_U200) { sendFormated("AT+UGPIOC=20,2\r\n"); if (OK != waitFinalResp()) return false; @@ -264,36 +253,34 @@ if (OK != waitFinalResp()) return false; } - // Enter PIN if needed - if (pin) { - sendFormated("AT+CPIN=%s\r\n", pin); - if (OK != waitFinalResp()) - return false; - } // check the sim card - for (int i = 0; i < 5; i++) { + for (int i = 0; (i < 5) && (_dev.sim != SIM_READY); i++) { sendFormated("AT+CPIN?\r\n"); - int ret = waitFinalResp(); + int ret = waitFinalResp(_cbCPIN, &_dev.sim); if ((OK != ret) && (ERROR != ret)) return false; - if (_sim != SIM_UNKNOWN) - break; - wait_ms(1000); + // Enter PIN if needed + if (_dev.sim == SIM_PIN) { + if (!pin) { + TRACE("SIM PIN not available\r\n"); + return false; + } + sendFormated("AT+CPIN=%s\r\n", pin); + if (OK != waitFinalResp(_cbCPIN, &_dev.sim)) + return false; + } else if (_dev.sim != SIM_READY) + wait_ms(1000); } - if (_sim != SIM_READY) + if (_dev.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(_cbCCID, _ccid)) + if (OK != waitFinalResp(_cbCCID, _dev.ccid)) return false; // Returns the product serial number, IMEI (International Mobile Equipment Identity) sendFormated("AT+CGSN\r\n"); - if (OK != waitFinalResp(_cbCGSN, _imei)) - return false; - // Setup SMS in text mode - sendFormated("AT+CMGF=1\r\n"); - if (OK != waitFinalResp()) + if (OK != waitFinalResp(_cbString, _dev.imei)) return false; // Configure New message indication //sendFormated("AT+CNMI=2,1,0,0,0\r\n"); @@ -301,53 +288,56 @@ // return false; } + // Setup SMS in text mode + sendFormated("AT+CMGF=1\r\n"); + if (OK != waitFinalResp()) + return false; + // setup new message indication + sendFormated("AT+CNMI=1,1\r\n"); + if (OK != waitFinalResp()) + return false; // Request IMSI (International Mobile Subscriber Identification) sendFormated("AT+CIMI\r\n"); - if (OK != waitFinalResp(_cbCIMI, _imsi)) + if (OK != waitFinalResp(_cbString, _dev.imsi)) return false; if (status) - { - status->model = _model; - status->sim = _sim; - status->ccid = _ccid; - status->imsi = _imsi; - status->imei = _imei; + memcpy(status, &_dev, sizeof(DevStatus)); + return true; +} + +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; + /*TRACE("Identified Model: SARA-G350 2G\\n")*/; + } else if (strstr(buf, "LISA-U200")) { + *model = MODEL_LISA_U200; + /*TRACE("Identified Model: LISA-U200 2G/3G\r\n")*/; + } else if (strstr(buf, "LISA-C200")) { + *model= MODEL_LISA_C200; + /*TRACE("Identified Model: LISA-C200 CDMA\r\n")*/; + } } - return true; + return WAIT; +} + +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; + } + } + return WAIT; } int MDMParser::_cbCCID(int type, const char* buf, int len, char* ccid) { if ((type == TYPE_PLUS) && ccid){ if (sscanf(buf, "\r\n+CCID: %[^\r]\r\n", ccid) == 1) - /*TRACE("Got CCID: %s\n", ccid)*/; - } - return WAIT; -} - -int MDMParser::_cbGSN(int type, const char* buf, int len, char* imei) -{ - if ((type == TYPE_UNKNOWN) && imei){ - if (sscanf(buf, "\r\n%[^\r]\r\n%*[^\r]\r\n", imei) == 1) - /*TRACE("Got IMEI: %s\n", imei)*/; - } - return WAIT; -} - -int MDMParser::_cbCGSN(int type, const char* buf, int len, char* imei) -{ - if ((type == TYPE_UNKNOWN) && imei){ - if (sscanf(buf, "\r\n%[^\r]\r\n", imei) == 1) - /*TRACE("Got IMEI: %s\n", imei)*/; - } - return WAIT; -} - -int MDMParser::_cbCIMI(int type, const char* buf, int len, char* imsi) -{ - if ((type == TYPE_UNKNOWN) && imsi) { - if (sscanf(buf, "\r\n%[^\r]\r\n", imsi) == 1) - /*TRACE("Got IMSI: %s\n", imsi)*/; + /*TRACE("Got CCID: %s\r\n", ccid)*/; } return WAIT; } @@ -358,39 +348,85 @@ sendFormated("AT+CREG?\r\n"); if (OK != waitFinalResp()) return false; - if ((_net != NET_ROAMING) && (_net != NET_HOME)) + if ((_net.reg != REG_ROAMING) && (_net.reg != REG_HOME)) return false; // check modem specific status messages - if (_model == MODEL_LISA_C200) { + if (_dev.model == MODEL_LISA_C200) { sendFormated("AT+CSS?\r\n"); if (OK != waitFinalResp()) return false; - if ((_net != NET_ROAMING) && (_net != NET_HOME)) + } else { + // check GPRS attach status + int state = 0; + sendFormated("AT+CGATT?\r\n"); + if (OK != waitFinalResp(_cbCGATT, &state)) return false; - } else { + if (state != 1) + return false; // check operator selection sendFormated("AT+COPS?\r\n"); - if (OK != waitFinalResp()) + if (OK != waitFinalResp(_cbCOPS, &_net)) return false; // Returns the MSISDNs related to this subscriber sendFormated("AT+CNUM\r\n"); - if (OK != waitFinalResp()) + if (OK != waitFinalResp(_cbCNUM, _net.num)) return false; } // Returns the signal strength indication sendFormated("AT+CSQ\r\n"); - if (OK != waitFinalResp()) + if (OK != waitFinalResp(_cbCSQ, &_net.rssi)) return false; if (status) { - status->num = _num; - status->opr = _opr; - status->rssi = _rssi; - status->net = _net; - status->act = _act; + memcpy(status, &_net, sizeof(NetStatus)); } return true; } +int MDMParser::_cbCGATT(int type, const char* buf, int len, int* state) +{ + if ((type == TYPE_PLUS) && state){ + if (sscanf(buf, "\r\n+CGATT: %d\r\n", state) == 1) + /*TRACE("Got CGATT: %d\r\n", state)*/; + } + return WAIT; +} + +int MDMParser::_cbCOPS(int type, const char* buf, int len, NetStatus* status) +{ + if ((type == TYPE_PLUS) && status){ + int act = 99; + // +COPS: <mode>[,<format>,<oper>[,<AcT>]] + if (sscanf(buf, "\r\n+COPS: %*d,%*d,\"%[^\"]\",%d",status->opr,&act) >= 1) { + if (act == 0) status->act = ACT_GSM; // 0: GSM, + else if (act == 2) status->act = ACT_UTRAN; // 2: UTRAN + } + } + return WAIT; +} + +int MDMParser::_cbCNUM(int type, const char* buf, int len, char* num) +{ + if ((type == TYPE_PLUS) && num){ + int a; + if ((sscanf(buf, "\r\n+CNUM: \"My Number\",\"%31[^\"]\",%d", num, &a) == 2) && + ((a == 129) || (a == 145))) { + } + } + return WAIT; +} + +int MDMParser::_cbCSQ(int type, const char* buf, int len, int* rssi) +{ + if ((type == TYPE_PLUS) && rssi){ + int a; + // +CSQ: <rssi>,<qual> + if (sscanf(buf, "\r\n+CSQ: %d,%*d",&a) == 1) { + if (a != 99) *rssi = -113 + 2*a; // 0: -113 1: -111 ... 30: -53 dBm with 2 dBm steps + //if (b != 99) int qual = b; // + } + } + return WAIT; +} bool MDMParser::powerOff(void) { sendFormated("AT+CPWROFF\r\n"); @@ -402,80 +438,106 @@ // ---------------------------------------------------------------- // internet connection -MDMParser::IP MDMParser::strToIp(const char* str) +MDMParser::IP MDMParser::join(const char* apn /*= NULL*/, const char* user /*= NULL*/, const char* password /*= NULL*/) { - 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++; - } - return ip; -} - -bool MDMParser::join(const char* apn /*= NULL*/, const char* user /*= NULL*/, const char* password /*= NULL*/) -{ - if (_model == MODEL_LISA_C200) { + IP ip = NOIP; + if (_dev.model == MODEL_LISA_C200) { #ifdef TODO // TODO implement // enable the sendFormated("AT$QCMIPEP=1\r\n"); if (OK != waitFinalResp()) - return false; + return NOIP; //Get local IP address sendFormated("AT+CMIP?\r\n"); + // extract: +CMIP: xxx.xxx.xxx.xxx if (OK != waitFinalResp()) - return false; + return NOIP; + #endif } else { // check gprs attach status sendFormated("AT+CGATT?\r\n"); if (OK != waitFinalResp()) - return false; - // Set up the APN + return NOIP; + + // Check the profile + int a = 0; + sendFormated("AT+UPSND=" PROFILE ",8\r\n"); + if (OK != waitFinalResp(_cbUPSND, &a)) + return NOIP; + if (a == 1) { + // disconnect the profile already if it is connected + sendFormated("AT+UPSDA=" PROFILE ",4\r\n"); + if (OK != waitFinalResp()) + return NOIP;; + } + // Set up the APN if (apn) { sendFormated("AT+UPSD=" PROFILE ",1,\"%s\"\r\n", apn); if (OK != waitFinalResp()) - return false; + return NOIP; } if (user) { sendFormated("AT+UPSD=" PROFILE ",2,\"%s\"\r\n", user); if (OK != waitFinalResp()) - return false; + return NOIP; } if (password) { sendFormated("AT+UPSD=" PROFILE ",3,\"%s\"\r\n", password); if (OK != waitFinalResp()) - return false; + return NOIP; } // Set up the dynamic IP address assignment. sendFormated("AT+UPSD=" PROFILE ",7,\"0.0.0.0\"\r\n"); if (OK != waitFinalResp()) - return false; + return NOIP; // Activate the profile and make connection sendFormated("AT+UPSDA=" PROFILE ",3\r\n"); if (OK != waitFinalResp()) - return false; + return NOIP; //Get local IP address sendFormated("AT+UPSND=" PROFILE ",0\r\n"); - if (OK != waitFinalResp()) - return false; + if (OK != waitFinalResp(_cbUPSND, &ip)) + return NOIP; + } + return ip; +} + +int MDMParser::_cbUPSND(int type, const char* buf, int len, int* act) +{ + if ((type == TYPE_PLUS) && act) { + if (sscanf(buf, "\r\n+UPSND: %*d,%*d,%d", act) == 1) + /*nothing*/; } - if (!_ip) - return false; - TRACE("Got IP address: " IPSTR "\n", IPNUM(_ip)); - return true; + return WAIT; +} + +int MDMParser::_cbUPSND(int type, const char* buf, int len, IP* ip) +{ + if ((type == TYPE_PLUS) && ip) { + int a,b,c,d; + // +UPSND=<profile_id>,<param_tag>[,<dynamic_param_val>] + if (sscanf(buf, "\r\n+UPSND: " PROFILE ",0,\"" IPSTR "\"", &a,&b,&c,&d) == 4) + *ip = IPADR(a,b,c,d); + } + return WAIT; +} + +int MDMParser::_cbUDNSRN(int type, const char* buf, int len, IP* ip) +{ + if ((type == TYPE_PLUS) && ip) { + int a,b,c,d; + if (sscanf(buf, "\r\n+UDNSRN: \""IPSTR"\"", &a,&b,&c,&d) == 4) + *ip = IPADR(a,b,c,d); + } + return WAIT; } bool MDMParser::disconnect(void) { - if (_ip == 0) + if (_ip == NOIP) return true; - if (_model == MODEL_LISA_C200) { + if (_dev.model == MODEL_LISA_C200) { #ifdef TODO // TODO implement sendFormated("AT$QCMIPEP=0\r\n"); #endif @@ -484,35 +546,22 @@ } if (OK != waitFinalResp()) return false; - _ip = 0; + _ip = NOIP; 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; -} - -bool MDMParser::gethostbyname(const char* host, IP* ip) +MDMParser::IP MDMParser::gethostbyname(const char* host) { - 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; + IP ip = NOIP; + int a,b,c,d; + if (sscanf(host, IPSTR, &a,&b,&c,&d) == 4) + ip = IPADR(a,b,c,d); + else { + sendFormated("AT+UDNSRN=0,\"%s\"\r\n", host); + if (OK != waitFinalResp(_cbUDNSRN, &ip)) + return false; } - sendFormated("AT+UDNSRN=0,\"%s\"\r\n", host); - if (OK != waitFinalResp(_cbUDNSRN, ip)) - return false; - return *ip != 0; + return ip; } // ---------------------------------------------------------------- @@ -552,8 +601,8 @@ bool MDMParser::socketConnect(int socket, const char * host, int port) { - IP ip; - if (!gethostbyname(host, &ip)) + IP ip = gethostbyname(host); + if (ip == NOIP) return false; // connect to socket if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_CREATED)) @@ -715,24 +764,29 @@ } // ---------------------------------------------------------------- -int MDMParser::_cbCPMS(int type, const char* buf, int len, int* num) + +int MDMParser::_cbCMGL(int type, const char* buf, int len, CMGLparam* param) { - if ((type == TYPE_PLUS) && num) { - // AT+CPMS: <used1>,<total1>,<used2>,<total2>,<used3>,<total3>; - if (sscanf(buf, "\r\n+CPMS: %d,%*d", num) == 1) - /*nothing*/; + if ((type == TYPE_PLUS) && param && param->num) { + // +CMGL: <ix>,... + int ix; + if (sscanf(buf, "\r\n+CMGL: %d,", &ix) == 1) + { + *param->ix++ = ix; + param->num--; + } } return WAIT; } -int MDMParser::smsCount(void) -{ - int num = 0; - sendFormated("AT+CPMS=\"ME\"\r\n"); - if (OK != waitFinalResp(_cbCPMS,&num)) { - return 0; - } - return num; +int MDMParser::smsList(const char* stat /*= "ALL"*/, int* ix /*=NULL*/, int num /*= 0*/) { + sendFormated("AT+CMGL=\"%s\"\r\n", stat); + CMGLparam param; + param.ix = ix; + param.num = num; + if (OK != waitFinalResp(_cbCMGL, ¶m)) + return -1; + return num - param.num; } bool MDMParser::smsSend(const char* num, const char* buf) @@ -797,14 +851,15 @@ } return WAIT; } -int MDMParser::ussdCommand(const char* cmd, char* buf, int len) + +bool MDMParser::ussdCommand(const char* cmd, char* buf) { *buf = '\0'; sendFormated("AT+CUSD=1,\"%s\"\r\n",cmd); if (OK != waitFinalResp(_cbCUSD, buf)) { - return -1; + return false; } - return strlen(buf); + return true; } // ---------------------------------------------------------------- @@ -884,7 +939,7 @@ } 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+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 }, @@ -902,7 +957,7 @@ if (ln == WAIT && fr) return WAIT; if ((ln != NOT_FOUND) && (unkn > 0)) - return pipe->get(buf, unkn); + return TYPE_UNKNOWN | pipe->get(buf, unkn); if (ln > 0) return lutF[i].type | pipe->get(buf, ln); } @@ -912,7 +967,7 @@ if (ln == WAIT && fr) return WAIT; if ((ln != NOT_FOUND) && (unkn > 0)) - return pipe->get(buf, unkn); + return TYPE_UNKNOWN | pipe->get(buf, unkn); if (ln > 0) return lut[i].type | pipe->get(buf, ln); }
--- a/MDM.h Tue Apr 08 15:49:04 2014 +0000 +++ b/MDM.h Wed Apr 09 11:48:04 2014 +0000 @@ -18,12 +18,222 @@ class MDMParser { public: - // waitFinalResp Responses + //! Constructor + MDMParser(void); + + // ---------------------------------------------------------------- + // Types + // ---------------------------------------------------------------- + + //! MT models + typedef enum { MODEL_UNKNOWN, MODEL_SARA_G350, MODEL_LISA_U200, MODEL_LISA_C200 } Model; + //! SIM Status + typedef enum { SIM_UNKNOWN, SIM_PIN, SIM_READY } Sim; + //! Device status + typedef struct { Model model; Sim sim; char ccid[20]; char imsi[16]; char imei[16]; } DevStatus; + //! Network Registration Status + typedef enum { REG_UNKNOWN, REG_DENIED, REG_NONE, REG_HOME, REG_ROAMING } Reg; + // Access Technology + typedef enum { ACT_UNKNOWN, ACT_GSM, ACT_EDGE, ACT_UTRAN, ACT_CDMA } AcT; + // Network Status + typedef struct { Reg reg; AcT act; int rssi; char opr[17]; char num[20]; } NetStatus; + //! An IP address + typedef uint32_t IP; + //! No IP address + #define NOIP ((MDMParser::IP)0) + // ip number formating and conversion + #define IPSTR "%d.%d.%d.%d" + #define IPNUM(ip) ((ip)>>24)&0xff, \ + ((ip)>>16)&0xff, \ + ((ip)>> 8)&0xff, \ + ((ip)>> 0)&0xff + #define IPADR(a,b,c,d) ((((IP)(a))<<24) | \ + (((IP)(b))<<16) | \ + (((IP)(c))<< 8) | \ + (((IP)(d))<< 0)) + + + // ---------------------------------------------------------------- + // Data Connection (GPRS) + // ---------------------------------------------------------------- + + /** register (Attach) the MT to the GPRS service. + \param pin a optional pin of the SIM card + \param status an optional struture to with device information + \return true if successful, false otherwise + */ + bool init(const char* pin = NULL, DevStatus* status = NULL); + + /** check if the network is available + \param status an optional structure to with network information + \return true if successful and connected to network, false otherwise + */ + bool checkNetStatus(NetStatus* status = NULL); + + /** Power off the MT, This function has to be called prior to + switching off the supply. + \return true if successfully, false otherwise + */ + bool powerOff(void); + + // ---------------------------------------------------------------- + // Data Connection (GPRS) + // ---------------------------------------------------------------- + + /** register (Attach) the MT to the GPRS service. + \param apn the of the network provider e.g. "internet" or "apn.provider.com" + \param user is the user name text string for the authentication phase + \param password is the password text string for the authentication phase + \return the ip that is assigned + */ + MDMParser::IP join(const char* apn = NULL, const char* user = NULL, const char* password = NULL); + + /** deregister (detach) the MT from the GPRS service. + \return true if successful, false otherwise + */ + bool disconnect(void); + + /** Translates a domain name to an IP address + \param host the domain name to translate e.g. "u-blox.com" + \return the IP if successful, 0 otherwise + */ + MDMParser::IP gethostbyname(const char* host); + + // ---------------------------------------------------------------- + // Sockets + // ---------------------------------------------------------------- + + //! Type of IP protocol + typedef enum { IPPROTO_TCP, IPPROTO_UDP } IpProtocol; + + //! Socket error return codes + #define SOCKET_ERROR -1 + + /** Create a socket for a ip protocol + \param ipproto the protocol (UDP or TCP) + \return the socket handle if successful or SOCKET_ERROR on failure + */ + int socketSocket(IpProtocol ipproto); + + /** make a socket connection + \param socket the socket handle + \param host the domain name to connect e.g. "u-blox.com" + \param port the port to connect + \return true if successfully, false otherwise + */ + bool socketConnect(int socket, const char* host, int port); + + /** Write socket data + \param socket the socket handle + \param buf the buffer to write + \param len the size of the buffer to write + \return the size written or SOCKET_ERROR on failure + */ + int socketSend(int socket, const char * buf, int len); + + /** Write socket data to a IP + \param socket the socket handle + \param ip the ip to send to + \param port the port to send to + \param buf the buffer to write + \param len the size of the buffer to write + \return the size written or SOCKET_ERROR on failure + */ + int socketSendTo(int socket, IP ip, int port, const char * buf, int len); + + /** Get the number of bytes pending for reading for this socket + \param socket the socket handle + \return the number of bytes pending or SOCKET_ERROR on failure + */ + int socketReadable(int socket); + + /** Read this socket + \param socket the socket handle + \param buf the buffer to read into + \param len the size of the buffer to read into + \return the number of bytes read or SOCKET_ERROR on failure + */ + int socketRecv(int socket, char* buf, int len); + + /** Read from this socket + \param socket the socket handle + \param buf the buffer to read into + \param len the size of the buffer to read into + \param ip the ip of host where the data originates from + \return the number of bytes read or SOCKET_ERROR on failure + */ + int socketRecvFrom(int socket, char* buf, int len, IP* ip); + + /** Close a connectied socket (that was connected with #socketConnect) + \param socket the socket handle + \return true if successfully, false otherwise + */ + bool socketClose(int socket); + + /** Free the socket (that was allocated before by #socketSocket) + \param socket the socket handle + \return true if successfully, false otherwise + */ + bool socketFree(int socket); + + // ---------------------------------------------------------------- + // SMS Short Message Service + // ---------------------------------------------------------------- + + /** count the number of sms in the device and optionally return a + list with indexes from the storage locations in the device. + \param stat what type of messages you can use use + "REC UNREAD", "REC READ", "STO UNSENT", "STO SENT", "ALL" + \param ix list where to save the storage positions + \param num number of elements in the list + \return the number of messages, this can be bigger than num, -1 on failure + */ + int smsList(const char* stat = "ALL", int* ix = NULL, int num = 0); + + /** Read a Message from a storage position + \param ix the storage position to read + \param num the originator address (~16 chars) + \param buf a buffer where to save the sm + \param len the length of the sm + \return true if successful, false otherwise + */ + bool smsRead(int ix, char* num, char* buf, int len); + + /** Send a message to a recipient + \param ix the storage position to delete + \return true if successful, false otherwise + */ + bool smsDelete(int ix); + + /** Send a message to a recipient + \param num the phone number of the recipient + \param buf the content of the message to sent + \return true if successful, false otherwise + */ + bool smsSend(const char* num, const char* buf); + + // ---------------------------------------------------------------- + // USSD Unstructured Supplementary Service Data + // ---------------------------------------------------------------- + + /** Read a Message from a storage position + \param cmd the ussd command to send e.g "*#06#" + \param buf a buffer where to save the reply + \return true if successful, false otherwise + */ + bool ussdCommand(const char* cmd, char* buf); + + // ---------------------------------------------------------------- + // Parseing + // ---------------------------------------------------------------- + + // 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) @@ -38,29 +248,58 @@ #define TYPE_NOANSWER 0x260000 #define TYPE_PROMPT 0x300000 #define TYPE_PLUS 0x400000 - // Socket Return Codes - #define SOCKET_ERROR -1 - #define SOCKET_OK 0 - typedef uint32_t IP; + + /** Get a line from the physical interface. This function need + to be implemented in a inherited class. Usually just calls + #_getLine on the rx buffer pipe. + + \param buf the buffer to store it + \param buf size of the buffer + \return type and length if something was found, + WAIT if not enough data is available + NOT_FOUND if nothing was found + */ + virtual int getLine(char* buf, int len) = 0; - typedef enum { MODEL_UNKNOWN, MODEL_SARA_G350, MODEL_LISA_U200, MODEL_LISA_C200 } Model; - typedef enum { SIM_UNKNOWN, SIM_PIN, SIM_READY } Sim; - typedef struct { Model model; Sim sim; const char* imsi; const char* imei; const char* ccid; } DevStatus; - typedef enum { NET_UNKNOWN, NET_DENIED, NET_NONE, NET_HOME, NET_ROAMING } Net; - typedef enum { ACT_UNKNOWN, ACT_GSM, ACT_EDGE, ACT_UTRAN, ACT_CDMA } AcT; - typedef struct { const char* num; const char* opr; int rssi; Net net; AcT act; } NetStatus; + /** Write data to the device + \param buf the buffer to write + \param buf size of the buffer to write + \return bytes written + */ + virtual int send(const char* buf, int len); - MDMParser(void); - - // interaction with AT command interface - virtual int getLine(char* buf, int len) = 0; - virtual int send(const char* buf, int len); + /** Write formated date to the physical interface (printf style) + \param fmt the format string + \param .. variable arguments to be formated + \return bytes written + */ int sendFormated(const char* format, ...); + /** callback function for #waitFinalResp with void* as argument + \param type the #getLine response + \param buf the parsed line + \param len the size of the parsed line + \param param the optional argument passed to #waitFinalResp + \return WAIT if processing should continue, + any other value aborts #waitFinalResp and this retunr value retuned + */ typedef int (*_CALLBACKPTR)(int type, const char* buf, int len, void* param); - int waitFinalResp(_CALLBACKPTR = NULL, + + /** Wait for a final respons + \param cb the optional callback function + \param param the optional callback function parameter + \param timeout_ms the timeout to wait + */ + int waitFinalResp(_CALLBACKPTR cb = NULL, void* param = NULL, int timeout_ms = 5000); + + /** template version of #waitFinalResp when using callbacks, + This template will allow the compiler to do type cheking but + internally symply casts the arguments and call the (void*) + version of #waitFinalResp. + \sa waitFinalResp + */ template<class T> int waitFinalResp(int (*cb)(int type, const char* buf, int len, T* param), @@ -68,40 +307,59 @@ { return waitFinalResp((_CALLBACKPTR)cb, (void*)param, timeout_ms); } - // network - bool init(const char* pin = NULL, DevStatus* status = NULL); - bool checkNetStatus(NetStatus* status = NULL); - bool powerOff(void); - // internet connection - bool join(const char* apn = NULL, const char* user = NULL, const char* password = NULL); - bool disconnect(void); - bool gethostbyname(const char* host, IP* ip); - // socket interface - 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 - int smsCount(void); - 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 _parseMatch(Pipe<char>* pipe, int len, const char* sta, const char* end); - static int _parseFormated(Pipe<char>* pipe, int len, const char* fmt); + /** Write bytes to the physical interface. This function should be + implemented in a inherited class. + \param buf the buffer to write + \param buf size of the buffer to write + \return bytes written + */ virtual int _send(const void* buf, int len) = 0; + + /** Helper: Parse a line from the receiving buffered pipe + \param pipe the receiving buffer pipe + \param buf the parsed line + \param len the size of the parsed line + \return type and length if something was found, + WAIT if not enough data is available + NOT_FOUND if nothing was found + */ + static int _getLine(Pipe<char>* pipe, char* buffer, int length); + + /** Helper: Parse a match from the pipe + \param pipe the buffered pipe + \param number of bytes to parse at maximum, + \param sta the starting string, NULL if none + \param end the terminating string, NULL if none + \return size of parsed match + */ + static int _parseMatch(Pipe<char>* pipe, int len, const char* sta, const char* end); + + /** Helper: Parse a match from the pipe + \param pipe the buffered pipe + \param number of bytes to parse at maximum, + \param fmt the formating string (%d any number, %c any char of last %d len) + \return size of parsed match + */ + static int _parseFormated(Pipe<char>* pipe, int len, const char* fmt); + private: + // parsing callbacks for different AT commands and their parameter arguments + static int _cbString(int type, const char* buf, int len, char* str); + static int _cbInt(int type, const char* buf, int len, int* val); + // device static int _cbATI(int type, const char* buf, int len, Model* model); - static int _cbCIMI(int type, const char* buf, int len, char* imsi); + static int _cbCPIN(int type, const char* buf, int len, Sim* sim); + static int _cbCCID(int type, const char* buf, int len, char* ccid); + // network + static int _cbCSQ(int type, const char* buf, int len, int* rssi); + static int _cbCOPS(int type, const char* buf, int len, NetStatus* status); + static int _cbCNUM(int type, const char* buf, int len, char* num); + static int _cbCGATT(int type, const char* buf, int len, int* state); + // sockets + static int _cbUPSND(int type, const char* buf, int len, int* act); + static int _cbUPSND(int type, const char* buf, int len, IP* ip); 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); @@ -109,24 +367,15 @@ 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 _cbCPMS(int type, const char* buf, int len, int* num); + // sms + typedef struct { int* ix; int num; } CMGLparam; + static int _cbCMGL(int type, const char* buf, int len, CMGLparam* param); static int _cbCMGR(int type, const char* buf, int len, CMGRparam* param); - static int _cbCGSN(int type, const char* buf, int len, char* imei); - static int _cbGSN(int type, const char* buf, int len, char* imei); - static int _cbCCID(int type, const char* buf, int len, char* ccid); - static IP strToIp(const char* str); - IP _ip; - Model _model; - Sim _sim; - Net _net; - AcT _act; - char _num[32]; - char _opr[32]; - int _rssi; - char _imsi[32]; - char _imei[32]; - char _ccid[32]; -private: + // + DevStatus _dev; //!< collected device information + NetStatus _net; //!< collected network information + IP _ip; //!< assigned ip address + // management struture for sockets typedef enum { SOCK_FREE, SOCK_CREATED, SOCK_CONNECTED } SockState; typedef struct { SockState state; int pending; } SockCtrl; SockCtrl _sockets[16];