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

Dependents:   HTTPClient_Cellular_HelloWorld Cellular_HelloMQTT MbedSmartRestMain Car_Bon_car_module ... more

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

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

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

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

Committer:
mazgch
Date:
Thu Jun 12 12:41:26 2014 +0000
Revision:
94:d697fe11f3e5
Parent:
90:3915192f6d7e
Child:
95:8282dbbe1492
add default arguments to connect

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mazgch 18:e5697801df29 1 #include "mbed.h"
mazgch 18:e5697801df29 2 #include "MDM.h"
mazgch 74:208e3e32d263 3 #include "Relax.h"
mazgch 74:208e3e32d263 4 #ifdef TARGET_UBLOX_C027
mazgch 74:208e3e32d263 5 #include "C027_api.h"
mazgch 74:208e3e32d263 6 #endif
mazgch 85:dd8f4f0d0ca9 7 #include "MDMAPN.h"
mazgch 84:a05edb010176 8
mazgch 74:208e3e32d263 9 #define PROFILE "0" //!< this is the psd profile used
mazgch 74:208e3e32d263 10 #define MAX_SIZE 128 //!< max expected messages
mazgch 74:208e3e32d263 11 //! test if it is a socket
mazgch 21:c4d64830bf02 12 #define ISSOCKET(s) (((s) >= 0) && ((s) < (sizeof(_sockets)/sizeof(*_sockets))))
mazgch 75:ce6e12067d0c 13 //! check for timeout
mazgch 75:ce6e12067d0c 14 #define TIMEOUT(t, ms) ((ms != TIMEOUT_BLOCKING) && (ms < t.read_ms()))
mazgch 80:34985b4d821e 15 //! registration ok check helper
mazgch 79:291df065e345 16 #define REG_OK(r) ((r == REG_HOME) || (r == REG_ROAMING))
mazgch 80:34985b4d821e 17 //! registration done check helper (no need to poll further)
mazgch 79:291df065e345 18 #define REG_DONE(r) ((r == REG_HOME) || (r == REG_ROAMING) || (r == REG_DENIED))
mazgch 79:291df065e345 19
mazgch 74:208e3e32d263 20 #ifdef MDM_DEBUG
mazgch 74:208e3e32d263 21 void dumpAtCmd(const char* buf, int len)
mazgch 21:c4d64830bf02 22 {
mazgch 75:ce6e12067d0c 23 ::printf(" %3d \"", len);
mazgch 21:c4d64830bf02 24 while (len --) {
mazgch 21:c4d64830bf02 25 char ch = *buf++;
mazgch 75:ce6e12067d0c 26 if ((ch > 0x1F) && (ch != 0x7F)) { // is printable
mazgch 75:ce6e12067d0c 27 if (ch == '%') ::printf("%%");
mazgch 75:ce6e12067d0c 28 else if (ch == '"') ::printf("\\\"");
mazgch 75:ce6e12067d0c 29 else if (ch == '\\') ::printf("\\\\");
mazgch 75:ce6e12067d0c 30 else putchar(ch);
mazgch 75:ce6e12067d0c 31 } else {
mazgch 75:ce6e12067d0c 32 if (ch == '\a') ::printf("\\a"); // BEL (0x07)
mazgch 75:ce6e12067d0c 33 else if (ch == '\b') ::printf("\\b"); // Backspace (0x08)
mazgch 75:ce6e12067d0c 34 else if (ch == '\t') ::printf("\\t"); // Horizontal Tab (0x09)
mazgch 75:ce6e12067d0c 35 else if (ch == '\n') ::printf("\\n"); // Linefeed (0x0A)
mazgch 75:ce6e12067d0c 36 else if (ch == '\v') ::printf("\\v"); // Vertical Tab (0x0B)
mazgch 75:ce6e12067d0c 37 else if (ch == '\f') ::printf("\\f"); // Formfeed (0x0C)
mazgch 75:ce6e12067d0c 38 else if (ch == '\r') ::printf("\\r"); // Carriage Return (0x0D)
mazgch 76:f7c3dd568dae 39 else ::printf("\\x%02x", (unsigned char)ch);
mazgch 75:ce6e12067d0c 40 }
mazgch 21:c4d64830bf02 41 }
mazgch 75:ce6e12067d0c 42 ::printf("\"\r\n");
mazgch 21:c4d64830bf02 43 }
mazgch 74:208e3e32d263 44
mazgch 75:ce6e12067d0c 45 #define ERROR(fmt) (_debugLevel < 0) ? : ::printf(RED(fmt))
mazgch 75:ce6e12067d0c 46 #define INFO(fmt) (_debugLevel < 1) ? : ::printf(GRE(fmt))
mazgch 75:ce6e12067d0c 47 #define TRACE(...) (_debugLevel < 2) ? : ::printf(__VA_ARGS__)
mazgch 74:208e3e32d263 48
mazgch 31:a0bed6c1e05d 49 #if 1 // colored terminal output using ANSI escape sequences
mazgch 75:ce6e12067d0c 50 #define COL(c,t) "\033[" c t "\033[" "39m"
mazgch 31:a0bed6c1e05d 51 #else
mazgch 31:a0bed6c1e05d 52 #define COL(c,t) t
mazgch 31:a0bed6c1e05d 53 #endif
mazgch 31:a0bed6c1e05d 54 #define BLA(t) COL("30m",t)
mazgch 31:a0bed6c1e05d 55 #define RED(t) COL("31m",t)
mazgch 31:a0bed6c1e05d 56 #define GRE(t) COL("32m",t)
mazgch 31:a0bed6c1e05d 57 #define YEL(t) COL("33m",t)
mazgch 31:a0bed6c1e05d 58 #define BLU(t) COL("34m",t)
mazgch 31:a0bed6c1e05d 59 #define MAG(t) COL("35m",t)
mazgch 31:a0bed6c1e05d 60 #define CYA(t) COL("36m",t)
mazgch 31:a0bed6c1e05d 61 #define WHY(t) COL("37m",t)
mazgch 74:208e3e32d263 62
mazgch 74:208e3e32d263 63 #else
mazgch 74:208e3e32d263 64
mazgch 75:ce6e12067d0c 65 #define ERROR(...) (void)0 // no tracing
mazgch 74:208e3e32d263 66 #define INFO(...) (void)0 // no tracing
mazgch 74:208e3e32d263 67 #define TRACE(...) (void)0 // no tracing
mazgch 74:208e3e32d263 68
mazgch 31:a0bed6c1e05d 69 #endif
mazgch 21:c4d64830bf02 70
mazgch 44:9d12223b78ff 71 MDMParser* MDMParser::inst;
mazgch 44:9d12223b78ff 72
mazgch 21:c4d64830bf02 73 MDMParser::MDMParser(void)
mazgch 21:c4d64830bf02 74 {
mazgch 44:9d12223b78ff 75 inst = this;
mazgch 31:a0bed6c1e05d 76 memset(&_dev, 0, sizeof(_dev));
mazgch 31:a0bed6c1e05d 77 memset(&_net, 0, sizeof(_net));
mazgch 54:7ba8e4c218e2 78 _net.lac = 0xFFFF;
mazgch 54:7ba8e4c218e2 79 _net.ci = 0xFFFFFFFF;
mazgch 35:9275215a3a5b 80 _ip = NOIP;
mazgch 76:f7c3dd568dae 81 _init = false;
mazgch 31:a0bed6c1e05d 82 memset(_sockets, 0, sizeof(_sockets));
mazgch 74:208e3e32d263 83 #ifdef MDM_DEBUG
mazgch 76:f7c3dd568dae 84 _debugLevel = 1;
mazgch 74:208e3e32d263 85 _debugTime.start();
mazgch 74:208e3e32d263 86 #endif
mazgch 74:208e3e32d263 87 }
mazgch 74:208e3e32d263 88
mazgch 18:e5697801df29 89 int MDMParser::send(const char* buf, int len)
mazgch 18:e5697801df29 90 {
mazgch 74:208e3e32d263 91 #ifdef MDM_DEBUG
mazgch 74:208e3e32d263 92 if (_debugLevel >= 3) {
mazgch 75:ce6e12067d0c 93 ::printf("%10.3f AT send ", _debugTime.read_ms()*0.001);
mazgch 74:208e3e32d263 94 dumpAtCmd(buf,len);
mazgch 74:208e3e32d263 95 }
mazgch 21:c4d64830bf02 96 #endif
mazgch 18:e5697801df29 97 return _send(buf, len);
mazgch 18:e5697801df29 98 }
mazgch 18:e5697801df29 99
mazgch 21:c4d64830bf02 100 int MDMParser::sendFormated(const char* format, ...) {
mazgch 21:c4d64830bf02 101 char buf[MAX_SIZE];
mazgch 21:c4d64830bf02 102 va_list args;
mazgch 21:c4d64830bf02 103 va_start(args, format);
mazgch 21:c4d64830bf02 104 int len = vsnprintf(buf,sizeof(buf), format, args);
mazgch 21:c4d64830bf02 105 va_end(args);
mazgch 21:c4d64830bf02 106 return send(buf, len);
mazgch 21:c4d64830bf02 107 }
mazgch 21:c4d64830bf02 108
mazgch 26:07be5faf8925 109 int MDMParser::waitFinalResp(_CALLBACKPTR cb /* = NULL*/,
mazgch 26:07be5faf8925 110 void* param /* = NULL*/,
mazgch 26:07be5faf8925 111 int timeout_ms /*= 5000*/)
mazgch 26:07be5faf8925 112 {
mazgch 69:4d6fa520dfca 113 char buf[MAX_SIZE + 64 /* add some more space for framing */];
mazgch 21:c4d64830bf02 114 Timer timer;
mazgch 21:c4d64830bf02 115 timer.start();
mazgch 21:c4d64830bf02 116 do {
mazgch 21:c4d64830bf02 117 int ret = getLine(buf, sizeof(buf));
mazgch 74:208e3e32d263 118 #ifdef MDM_DEBUG
mazgch 74:208e3e32d263 119 if ((_debugLevel >= 3) && (ret != WAIT) && (ret != NOT_FOUND))
mazgch 21:c4d64830bf02 120 {
mazgch 31:a0bed6c1e05d 121 int len = LENGTH(ret);
mazgch 31:a0bed6c1e05d 122 int type = TYPE(ret);
mazgch 31:a0bed6c1e05d 123 const char* s = (type == TYPE_UNKNOWN)? YEL("UNK") :
mazgch 35:9275215a3a5b 124 (type == TYPE_TEXT) ? MAG("TXT") :
mazgch 53:cb0d94b9de3a 125 (type == TYPE_OK ) ? GRE("OK ") :
mazgch 31:a0bed6c1e05d 126 (type == TYPE_ERROR) ? RED("ERR") :
mazgch 31:a0bed6c1e05d 127 (type == TYPE_PLUS) ? CYA(" + ") :
mazgch 31:a0bed6c1e05d 128 (type == TYPE_PROMPT) ? BLU(" > ") :
mazgch 31:a0bed6c1e05d 129 "..." ;
mazgch 75:ce6e12067d0c 130 ::printf("%10.3f AT read %s", _debugTime.read_ms()*0.001, s);
mazgch 74:208e3e32d263 131 dumpAtCmd(buf, len);
mazgch 21:c4d64830bf02 132 }
mazgch 21:c4d64830bf02 133 #endif
mazgch 21:c4d64830bf02 134 if ((ret != WAIT) && (ret != NOT_FOUND))
mazgch 18:e5697801df29 135 {
mazgch 21:c4d64830bf02 136 int type = TYPE(ret);
mazgch 21:c4d64830bf02 137 // handle unsolicited commands here
mazgch 21:c4d64830bf02 138 if (type == TYPE_PLUS) {
mazgch 21:c4d64830bf02 139 const char* cmd = buf+3;
mazgch 54:7ba8e4c218e2 140 int a, b, c, d, r;
mazgch 23:05a1aeeb5fd9 141 char s[32];
mazgch 21:c4d64830bf02 142
mazgch 31:a0bed6c1e05d 143 // SMS Command ---------------------------------
mazgch 31:a0bed6c1e05d 144 // +CNMI: <mem>,<index>
mazgch 31:a0bed6c1e05d 145 if (sscanf(cmd, "CMTI: \"%*[^\"]\",%d", &a) == 1) {
mazgch 31:a0bed6c1e05d 146 TRACE("New SMS at index %d\r\n", a);
mazgch 21:c4d64830bf02 147 // Socket Specific Command ---------------------------------
mazgch 21:c4d64830bf02 148 // +UUSORD: <socket>,<length>
mazgch 21:c4d64830bf02 149 } else if ((sscanf(cmd, "UUSORD: %d,%d", &a, &b) == 2) &&
mazgch 63:42cb563a25bc 150 ISSOCKET(a) /*&& (_sockets[a].state == SOCK_CONNECTED)*/) {
mazgch 31:a0bed6c1e05d 151 TRACE("Socket %d: %d bytes pending\r\n", a, b);
mazgch 21:c4d64830bf02 152 _sockets[a].pending = b;
mazgch 69:4d6fa520dfca 153 // +UUSORF: <socket>,<length>
mazgch 69:4d6fa520dfca 154 } else if ((sscanf(cmd, "UUSORF: %d,%d", &a, &b) == 2) &&
mazgch 69:4d6fa520dfca 155 ISSOCKET(a) /*&& (_sockets[a].state == SOCK_CONNECTED)*/) {
mazgch 69:4d6fa520dfca 156 TRACE("Socket %d: %d bytes pending\r\n", a, b);
mazgch 69:4d6fa520dfca 157 _sockets[a].pending = b;
mazgch 21:c4d64830bf02 158 // +UUSOCL: <socket>
mazgch 21:c4d64830bf02 159 } else if ((sscanf(cmd, "UUSOCL: %d", &a) == 1) &&
mazgch 21:c4d64830bf02 160 ISSOCKET(a) && (_sockets[a].state == SOCK_CONNECTED)) {
mazgch 31:a0bed6c1e05d 161 TRACE("Socket %d: closed by remote host\r\n", a);
mazgch 21:c4d64830bf02 162 _sockets[a].state = SOCK_CREATED/*=CLOSED*/;
mazgch 21:c4d64830bf02 163 }
mazgch 32:8f12ac182bbb 164 if (_dev.dev == DEV_LISA_C200) {
mazgch 21:c4d64830bf02 165 // CDMA Specific -------------------------------------------
mazgch 21:c4d64830bf02 166 // +CREG: <n><SID>,<NID>,<stat>
mazgch 54:7ba8e4c218e2 167 if (sscanf(cmd, "CREG: %*d,%d,%d,%d",&a,&b,&c) == 3) {
mazgch 54:7ba8e4c218e2 168 // _net.sid = a;
mazgch 54:7ba8e4c218e2 169 // _net.nid = b;
mazgch 79:291df065e345 170 if (c == 0) _net.csd = REG_NONE; // not registered, home network
mazgch 79:291df065e345 171 else if (c == 1) _net.csd = REG_HOME; // registered, home network
mazgch 79:291df065e345 172 else if (c == 2) _net.csd = REG_NONE; // not registered, but MT is currently searching a new operator to register to
mazgch 79:291df065e345 173 else if (c == 3) _net.csd = REG_DENIED; // registration denied
mazgch 79:291df065e345 174 else if (c == 5) _net.csd = REG_ROAMING; // registered, roaming
mazgch 79:291df065e345 175 _net.psd = _net.csd; // fake PSD registration (CDMA is always registered)
mazgch 31:a0bed6c1e05d 176 _net.act = ACT_CDMA;
mazgch 84:a05edb010176 177 // +CSS: <mode>[,<format>,<oper>[,<AcT>]]
mazgch 21:c4d64830bf02 178 } else if (sscanf(cmd, "CSS %*c,%2s,%*d",s) == 1) {
mazgch 31:a0bed6c1e05d 179 //_net.reg = (strcmp("Z", s) == 0) ? REG_UNKNOWN : REG_HOME;
mazgch 21:c4d64830bf02 180 }
mazgch 21:c4d64830bf02 181 } else {
mazgch 21:c4d64830bf02 182 // GSM/UMTS Specific -------------------------------------------
mazgch 79:291df065e345 183 // +UUPSDD: <profile_id>
mazgch 79:291df065e345 184 if (sscanf(cmd, "UUPSDD: %d",&a) == 1) {
mazgch 79:291df065e345 185 if (*PROFILE == a) _ip = NOIP;
mazgch 79:291df065e345 186 } else {
mazgch 79:291df065e345 187 // +CREG|CGREG: <n>,<stat>[,<lac>,<ci>[,AcT[,<rac>]]] // reply to AT+CREG|AT+CGREG
mazgch 79:291df065e345 188 // +CREG|CGREG: <stat>[,<lac>,<ci>[,AcT[,<rac>]]] // URC
mazgch 79:291df065e345 189 b = 0xFFFF; c = 0xFFFFFFFF; d = -1;
mazgch 79:291df065e345 190 r = sscanf(cmd, "%s %*d,%d,\"%X\",\"%X\",%d",s,&a,&b,&c,&d);
mazgch 79:291df065e345 191 if (r <= 1)
mazgch 79:291df065e345 192 r = sscanf(cmd, "%s %d,\"%X\",\"%X\",%d",s,&a,&b,&c,&d);
mazgch 79:291df065e345 193 if (r >= 2) {
mazgch 79:291df065e345 194 Reg *reg = !strcmp(s, "CREG:") ? &_net.csd :
mazgch 79:291df065e345 195 !strcmp(s, "CGREG:") ? &_net.psd : NULL;
mazgch 79:291df065e345 196 if (reg) {
mazgch 79:291df065e345 197 // network status
mazgch 79:291df065e345 198 if (a == 0) *reg = REG_NONE; // 0: not registered, home network
mazgch 79:291df065e345 199 else if (a == 1) *reg = REG_HOME; // 1: registered, home network
mazgch 79:291df065e345 200 else if (a == 2) *reg = REG_NONE; // 2: not registered, but MT is currently searching a new operator to register to
mazgch 79:291df065e345 201 else if (a == 3) *reg = REG_DENIED; // 3: registration denied
mazgch 79:291df065e345 202 else if (a == 4) *reg = REG_UNKNOWN; // 4: unknown
mazgch 79:291df065e345 203 else if (a == 5) *reg = REG_ROAMING; // 5: registered, roaming
mazgch 79:291df065e345 204 if ((r >= 3) && (b != 0xFFFF)) _net.lac = b; // location area code
mazgch 79:291df065e345 205 if ((r >= 4) && (c != 0xFFFFFFFF)) _net.ci = c; // cell ID
mazgch 79:291df065e345 206 // access technology
mazgch 79:291df065e345 207 if (r >= 5) {
mazgch 79:291df065e345 208 if (d == 0) _net.act = ACT_GSM; // 0: GSM
mazgch 79:291df065e345 209 else if (d == 1) _net.act = ACT_GSM; // 1: GSM COMPACT
mazgch 79:291df065e345 210 else if (d == 2) _net.act = ACT_UTRAN; // 2: UTRAN
mazgch 79:291df065e345 211 else if (d == 3) _net.act = ACT_EDGE; // 3: GSM with EDGE availability
mazgch 79:291df065e345 212 else if (d == 4) _net.act = ACT_UTRAN; // 4: UTRAN with HSDPA availability
mazgch 79:291df065e345 213 else if (d == 5) _net.act = ACT_UTRAN; // 5: UTRAN with HSUPA availability
mazgch 79:291df065e345 214 else if (d == 6) _net.act = ACT_UTRAN; // 6: UTRAN with HSDPA and HSUPA availability
mazgch 79:291df065e345 215 }
mazgch 79:291df065e345 216 }
mazgch 54:7ba8e4c218e2 217 }
mazgch 21:c4d64830bf02 218 }
mazgch 21:c4d64830bf02 219 }
mazgch 21:c4d64830bf02 220 }
mazgch 21:c4d64830bf02 221 if (cb) {
mazgch 21:c4d64830bf02 222 int len = LENGTH(ret);
mazgch 21:c4d64830bf02 223 int ret = cb(type, buf, len, param);
mazgch 21:c4d64830bf02 224 if (WAIT != ret)
mazgch 21:c4d64830bf02 225 return ret;
mazgch 21:c4d64830bf02 226 }
mazgch 75:ce6e12067d0c 227 if (type == TYPE_OK) return RESP_OK;
mazgch 75:ce6e12067d0c 228 if (type == TYPE_ERROR) return RESP_ERROR;
mazgch 75:ce6e12067d0c 229 if (type == TYPE_PROMPT) return RESP_PROMPT;
mazgch 21:c4d64830bf02 230 }
mazgch 21:c4d64830bf02 231 // relax a bit
mazgch 74:208e3e32d263 232 RELAX_MS(10);
mazgch 21:c4d64830bf02 233 }
mazgch 75:ce6e12067d0c 234 while (!TIMEOUT(timer, timeout_ms));
mazgch 21:c4d64830bf02 235 return WAIT;
mazgch 21:c4d64830bf02 236 }
mazgch 21:c4d64830bf02 237
mazgch 31:a0bed6c1e05d 238 int MDMParser::_cbString(int type, const char* buf, int len, char* str)
mazgch 21:c4d64830bf02 239 {
mazgch 37:cc3433329d66 240 if (str && (type == TYPE_UNKNOWN)) {
mazgch 31:a0bed6c1e05d 241 if (sscanf(buf, "\r\n%s\r\n", str) == 1)
mazgch 31:a0bed6c1e05d 242 /*nothing*/;
mazgch 21:c4d64830bf02 243 }
mazgch 21:c4d64830bf02 244 return WAIT;
mazgch 21:c4d64830bf02 245 }
mazgch 21:c4d64830bf02 246
mazgch 31:a0bed6c1e05d 247 int MDMParser::_cbInt(int type, const char* buf, int len, int* val)
mazgch 31:a0bed6c1e05d 248 {
mazgch 37:cc3433329d66 249 if (val && (type == TYPE_UNKNOWN)) {
mazgch 31:a0bed6c1e05d 250 if (sscanf(buf, "\r\n%d\r\n", val) == 1)
mazgch 31:a0bed6c1e05d 251 /*nothing*/;
mazgch 31:a0bed6c1e05d 252 }
mazgch 31:a0bed6c1e05d 253 return WAIT;
mazgch 31:a0bed6c1e05d 254 }
mazgch 31:a0bed6c1e05d 255
mazgch 31:a0bed6c1e05d 256 // ----------------------------------------------------------------
mazgch 31:a0bed6c1e05d 257
mazgch 57:869bd35f44cc 258 bool MDMParser::connect(
mazgch 57:869bd35f44cc 259 const char* simpin,
mazgch 80:34985b4d821e 260 const char* apn, const char* username,
mazgch 80:34985b4d821e 261 const char* password, Auth auth,
mazgch 74:208e3e32d263 262 PinName pn)
mazgch 57:869bd35f44cc 263 {
mazgch 75:ce6e12067d0c 264 bool ok = init(simpin, NULL, pn);
mazgch 74:208e3e32d263 265 #ifdef MDM_DEBUG
mazgch 75:ce6e12067d0c 266 if (_debugLevel >= 1) dumpDevStatus(&_dev);
mazgch 74:208e3e32d263 267 #endif
mazgch 75:ce6e12067d0c 268 if (!ok)
mazgch 57:869bd35f44cc 269 return false;
mazgch 75:ce6e12067d0c 270 ok = registerNet();
mazgch 74:208e3e32d263 271 #ifdef MDM_DEBUG
mazgch 75:ce6e12067d0c 272 if (_debugLevel >= 1) dumpNetStatus(&_net);
mazgch 74:208e3e32d263 273 #endif
mazgch 75:ce6e12067d0c 274 if (!ok)
mazgch 57:869bd35f44cc 275 return false;
mazgch 80:34985b4d821e 276 IP ip = join(apn,username,password,auth);
mazgch 74:208e3e32d263 277 #ifdef MDM_DEBUG
mazgch 74:208e3e32d263 278 if (_debugLevel >= 1) dumpIp(ip);
mazgch 74:208e3e32d263 279 #endif
mazgch 57:869bd35f44cc 280 if (ip == NOIP)
mazgch 57:869bd35f44cc 281 return false;
mazgch 57:869bd35f44cc 282 return true;
mazgch 57:869bd35f44cc 283 }
mazgch 57:869bd35f44cc 284
mazgch 74:208e3e32d263 285 bool MDMParser::init(const char* simpin, DevStatus* status, PinName pn)
mazgch 21:c4d64830bf02 286 {
mazgch 74:208e3e32d263 287 int i = 10;
mazgch 76:f7c3dd568dae 288 memset(&_dev, 0, sizeof(_dev));
mazgch 74:208e3e32d263 289 if (pn != NC) {
mazgch 74:208e3e32d263 290 INFO("Modem::wakeup\r\n");
mazgch 74:208e3e32d263 291 DigitalOut pin(pn, 1);
mazgch 74:208e3e32d263 292 while (i--) {
mazgch 79:291df065e345 293 // SARA-U2/LISA-U2 50..80us
mazgch 79:291df065e345 294 pin = 0; wait_us(50);
mazgch 79:291df065e345 295 pin = 1; wait_ms(10);
mazgch 79:291df065e345 296
mazgch 79:291df065e345 297 // SARA-G35 >5ms, LISA-C2 > 150ms
mazgch 79:291df065e345 298 pin = 0; wait_ms(150);
mazgch 79:291df065e345 299 pin = 1; wait_ms(100);
mazgch 79:291df065e345 300
mazgch 79:291df065e345 301 // purge any messages
mazgch 79:291df065e345 302 while (WAIT != waitFinalResp(NULL,NULL,0))
mazgch 79:291df065e345 303 /* nothing */;
mazgch 74:208e3e32d263 304 // check interface and disable local echo
mazgch 74:208e3e32d263 305 sendFormated("AT\r\n");
mazgch 74:208e3e32d263 306 if(RESP_OK == waitFinalResp(NULL,NULL,500))
mazgch 74:208e3e32d263 307 break;
mazgch 74:208e3e32d263 308 }
mazgch 75:ce6e12067d0c 309 if (i < 0) {
mazgch 75:ce6e12067d0c 310 ERROR("No Reply from Modem");
mazgch 74:208e3e32d263 311 return false;
mazgch 75:ce6e12067d0c 312 }
mazgch 21:c4d64830bf02 313 }
mazgch 76:f7c3dd568dae 314 _init = true;
mazgch 75:ce6e12067d0c 315
mazgch 74:208e3e32d263 316 INFO("Modem::init\r\n");
mazgch 21:c4d64830bf02 317 // echo off
mazgch 21:c4d64830bf02 318 sendFormated("AT E0\r\n");
mazgch 52:8071747a7cb3 319 if(RESP_OK != waitFinalResp())
mazgch 21:c4d64830bf02 320 return false;
mazgch 21:c4d64830bf02 321 // enable verbose error messages
mazgch 21:c4d64830bf02 322 sendFormated("AT+CMEE=2\r\n");
mazgch 52:8071747a7cb3 323 if(RESP_OK != waitFinalResp())
mazgch 21:c4d64830bf02 324 return false;
mazgch 21:c4d64830bf02 325 // set baud rate
mazgch 21:c4d64830bf02 326 sendFormated("AT+IPR=115200\r\n");
mazgch 52:8071747a7cb3 327 if (RESP_OK != waitFinalResp())
mazgch 21:c4d64830bf02 328 return false;
mazgch 74:208e3e32d263 329 RELAX_MS(40);
mazgch 21:c4d64830bf02 330 // identify the module
mazgch 21:c4d64830bf02 331 sendFormated("ATI\r\n");
mazgch 52:8071747a7cb3 332 if (RESP_OK != waitFinalResp(_cbATI, &_dev.dev))
mazgch 21:c4d64830bf02 333 return false;
mazgch 32:8f12ac182bbb 334 if (_dev.dev == DEV_UNKNOWN)
mazgch 21:c4d64830bf02 335 return false;
mazgch 32:8f12ac182bbb 336 // device specific init
mazgch 32:8f12ac182bbb 337 if (_dev.dev == DEV_LISA_C200) {
mazgch 32:8f12ac182bbb 338 // get the manufacturer
mazgch 32:8f12ac182bbb 339 sendFormated("AT+GMI\r\n");
mazgch 52:8071747a7cb3 340 if (RESP_OK != waitFinalResp(_cbString, _dev.manu))
mazgch 32:8f12ac182bbb 341 return false;
mazgch 32:8f12ac182bbb 342 // get the model identification
mazgch 32:8f12ac182bbb 343 sendFormated("AT+GMM\r\n");
mazgch 52:8071747a7cb3 344 if (RESP_OK != waitFinalResp(_cbString, _dev.model))
mazgch 32:8f12ac182bbb 345 return false;
mazgch 32:8f12ac182bbb 346 // get the sw version
mazgch 32:8f12ac182bbb 347 sendFormated("AT+GMR\r\n");
mazgch 52:8071747a7cb3 348 if (RESP_OK != waitFinalResp(_cbString, _dev.ver))
mazgch 32:8f12ac182bbb 349 return false;
mazgch 21:c4d64830bf02 350 // Return the pseudo ESN or MEID
mazgch 21:c4d64830bf02 351 sendFormated("AT+GSN\r\n");
mazgch 52:8071747a7cb3 352 if (RESP_OK != waitFinalResp(_cbString, _dev.meid))
mazgch 26:07be5faf8925 353 return false;
mazgch 50:d76aece8038f 354 #if 0
mazgch 50:d76aece8038f 355 // enable power saving
mazgch 50:d76aece8038f 356 if (_dev.lpm != LPM_DISABLED) {
mazgch 50:d76aece8038f 357 // enable power saving (requires flow control, cts at least)
mazgch 50:d76aece8038f 358 sendFormated("AT+UPSV=1,1280\r\n");
mazgch 52:8071747a7cb3 359 if (RESP_OK != waitFinalResp())
mazgch 50:d76aece8038f 360 return false;
mazgch 50:d76aece8038f 361 _dev.lpm = LPM_ACTIVE;
mazgch 50:d76aece8038f 362 }
mazgch 50:d76aece8038f 363 #endif
mazgch 26:07be5faf8925 364 } else {
mazgch 32:8f12ac182bbb 365 if (_dev.dev == DEV_LISA_U200) {
mazgch 35:9275215a3a5b 366 // enable the network identification feature
mazgch 21:c4d64830bf02 367 sendFormated("AT+UGPIOC=20,2\r\n");
mazgch 52:8071747a7cb3 368 if (RESP_OK != waitFinalResp())
mazgch 21:c4d64830bf02 369 return false;
mazgch 21:c4d64830bf02 370 } else {
mazgch 35:9275215a3a5b 371 // enable the network identification feature
mazgch 21:c4d64830bf02 372 sendFormated("AT+UGPIOC=16,2\r\n");
mazgch 52:8071747a7cb3 373 if (RESP_OK != waitFinalResp())
mazgch 21:c4d64830bf02 374 return false;
mazgch 21:c4d64830bf02 375 }
mazgch 21:c4d64830bf02 376 // check the sim card
mazgch 31:a0bed6c1e05d 377 for (int i = 0; (i < 5) && (_dev.sim != SIM_READY); i++) {
mazgch 21:c4d64830bf02 378 sendFormated("AT+CPIN?\r\n");
mazgch 31:a0bed6c1e05d 379 int ret = waitFinalResp(_cbCPIN, &_dev.sim);
mazgch 62:a02f026bdd7c 380 // having an error here is ok (sim may still be initializing)
mazgch 52:8071747a7cb3 381 if ((RESP_OK != ret) && (RESP_ERROR != ret))
mazgch 21:c4d64830bf02 382 return false;
mazgch 31:a0bed6c1e05d 383 // Enter PIN if needed
mazgch 77:55788e619858 384 if (_dev.sim == SIM_PIN) {
mazgch 57:869bd35f44cc 385 if (!simpin) {
mazgch 75:ce6e12067d0c 386 ERROR("SIM PIN not available\r\n");
mazgch 31:a0bed6c1e05d 387 return false;
mazgch 31:a0bed6c1e05d 388 }
mazgch 57:869bd35f44cc 389 sendFormated("AT+CPIN=%s\r\n", simpin);
mazgch 52:8071747a7cb3 390 if (RESP_OK != waitFinalResp(_cbCPIN, &_dev.sim))
mazgch 31:a0bed6c1e05d 391 return false;
mazgch 62:a02f026bdd7c 392 } else if (_dev.sim != SIM_READY) {
mazgch 74:208e3e32d263 393 RELAX_MS(1000);
mazgch 62:a02f026bdd7c 394 }
mazgch 21:c4d64830bf02 395 }
mazgch 77:55788e619858 396 if (_dev.sim != SIM_READY) {
mazgch 77:55788e619858 397 if (_dev.sim == SIM_MISSING)
mazgch 77:55788e619858 398 ERROR("SIM not inserted\r\n");
mazgch 21:c4d64830bf02 399 return false;
mazgch 77:55788e619858 400 }
mazgch 32:8f12ac182bbb 401 // get the manufacturer
mazgch 32:8f12ac182bbb 402 sendFormated("AT+CGMI\r\n");
mazgch 52:8071747a7cb3 403 if (RESP_OK != waitFinalResp(_cbString, _dev.manu))
mazgch 32:8f12ac182bbb 404 return false;
mazgch 32:8f12ac182bbb 405 // get the model identification
mazgch 32:8f12ac182bbb 406 sendFormated("AT+CGMM\r\n");
mazgch 52:8071747a7cb3 407 if (RESP_OK != waitFinalResp(_cbString, _dev.model))
mazgch 32:8f12ac182bbb 408 return false;
mazgch 32:8f12ac182bbb 409 // get the
mazgch 32:8f12ac182bbb 410 sendFormated("AT+CGMR\r\n");
mazgch 52:8071747a7cb3 411 if (RESP_OK != waitFinalResp(_cbString, _dev.ver))
mazgch 32:8f12ac182bbb 412 return false;
mazgch 21:c4d64830bf02 413 // Returns the ICCID (Integrated Circuit Card ID) of the SIM-card.
mazgch 21:c4d64830bf02 414 // ICCID is a serial number identifying the SIM.
mazgch 21:c4d64830bf02 415 sendFormated("AT+CCID\r\n");
mazgch 52:8071747a7cb3 416 if (RESP_OK != waitFinalResp(_cbCCID, _dev.ccid))
mazgch 21:c4d64830bf02 417 return false;
mazgch 21:c4d64830bf02 418 // Returns the product serial number, IMEI (International Mobile Equipment Identity)
mazgch 21:c4d64830bf02 419 sendFormated("AT+CGSN\r\n");
mazgch 52:8071747a7cb3 420 if (RESP_OK != waitFinalResp(_cbString, _dev.imei))
mazgch 21:c4d64830bf02 421 return false;
mazgch 50:d76aece8038f 422 // enable power saving
mazgch 50:d76aece8038f 423 if (_dev.lpm != LPM_DISABLED) {
mazgch 50:d76aece8038f 424 // enable power saving (requires flow control, cts at least)
mazgch 50:d76aece8038f 425 sendFormated("AT+UPSV=1\r\n");
mazgch 52:8071747a7cb3 426 if (RESP_OK != waitFinalResp())
mazgch 50:d76aece8038f 427 return false;
mazgch 50:d76aece8038f 428 _dev.lpm = LPM_ACTIVE;
mazgch 50:d76aece8038f 429 }
mazgch 79:291df065e345 430 // enable the psd registration unsolicited result code
mazgch 79:291df065e345 431 sendFormated("AT+CGREG=2\r\n");
mazgch 79:291df065e345 432 if (RESP_OK != waitFinalResp())
mazgch 79:291df065e345 433 return false;
mazgch 21:c4d64830bf02 434 }
mazgch 79:291df065e345 435 // enable the network registration unsolicited result code
mazgch 79:291df065e345 436 sendFormated("AT+CREG=%d\r\n", (_dev.dev == DEV_LISA_C200) ? 1 : 2);
mazgch 79:291df065e345 437 if (RESP_OK != waitFinalResp())
mazgch 79:291df065e345 438 return false;
mazgch 31:a0bed6c1e05d 439 // Setup SMS in text mode
mazgch 31:a0bed6c1e05d 440 sendFormated("AT+CMGF=1\r\n");
mazgch 52:8071747a7cb3 441 if (RESP_OK != waitFinalResp())
mazgch 31:a0bed6c1e05d 442 return false;
mazgch 31:a0bed6c1e05d 443 // setup new message indication
mazgch 75:ce6e12067d0c 444 sendFormated("AT+CNMI=2,1\r\n");
mazgch 52:8071747a7cb3 445 if (RESP_OK != waitFinalResp())
mazgch 31:a0bed6c1e05d 446 return false;
mazgch 21:c4d64830bf02 447 // Request IMSI (International Mobile Subscriber Identification)
mazgch 21:c4d64830bf02 448 sendFormated("AT+CIMI\r\n");
mazgch 52:8071747a7cb3 449 if (RESP_OK != waitFinalResp(_cbString, _dev.imsi))
mazgch 21:c4d64830bf02 450 return false;
mazgch 28:4d9509e3b1cf 451 if (status)
mazgch 31:a0bed6c1e05d 452 memcpy(status, &_dev, sizeof(DevStatus));
mazgch 31:a0bed6c1e05d 453 return true;
mazgch 31:a0bed6c1e05d 454 }
mazgch 31:a0bed6c1e05d 455
mazgch 74:208e3e32d263 456 bool MDMParser::powerOff(void)
mazgch 74:208e3e32d263 457 {
mazgch 76:f7c3dd568dae 458 if (_init) {
mazgch 76:f7c3dd568dae 459 INFO("Modem::powerOff\r\n");
mazgch 76:f7c3dd568dae 460 sendFormated("AT+CPWROFF\r\n");
mazgch 76:f7c3dd568dae 461 if (RESP_OK != waitFinalResp(NULL,NULL,120*1000))
mazgch 76:f7c3dd568dae 462 return false;
mazgch 76:f7c3dd568dae 463 _init = false;
mazgch 76:f7c3dd568dae 464 }
mazgch 74:208e3e32d263 465 return true;
mazgch 74:208e3e32d263 466 }
mazgch 74:208e3e32d263 467
mazgch 32:8f12ac182bbb 468 int MDMParser::_cbATI(int type, const char* buf, int len, Dev* dev)
mazgch 31:a0bed6c1e05d 469 {
mazgch 37:cc3433329d66 470 if ((type == TYPE_UNKNOWN) && dev) {
mazgch 31:a0bed6c1e05d 471 if (strstr(buf, "SARA-G350")) {
mazgch 32:8f12ac182bbb 472 *dev = DEV_SARA_G350;
mazgch 32:8f12ac182bbb 473 /*TRACE("Identified Device: SARA-G350 2G\\n")*/;
mazgch 31:a0bed6c1e05d 474 } else if (strstr(buf, "LISA-U200")) {
mazgch 32:8f12ac182bbb 475 *dev = DEV_LISA_U200;
mazgch 32:8f12ac182bbb 476 /*TRACE("Identified Device: LISA-U200 2G/3G\r\n")*/;
mazgch 31:a0bed6c1e05d 477 } else if (strstr(buf, "LISA-C200")) {
mazgch 32:8f12ac182bbb 478 *dev= DEV_LISA_C200;
mazgch 32:8f12ac182bbb 479 /*TRACE("Identified Device: LISA-C200 CDMA\r\n")*/;
mazgch 31:a0bed6c1e05d 480 }
mazgch 28:4d9509e3b1cf 481 }
mazgch 31:a0bed6c1e05d 482 return WAIT;
mazgch 31:a0bed6c1e05d 483 }
mazgch 31:a0bed6c1e05d 484
mazgch 31:a0bed6c1e05d 485 int MDMParser::_cbCPIN(int type, const char* buf, int len, Sim* sim)
mazgch 31:a0bed6c1e05d 486 {
mazgch 75:ce6e12067d0c 487 if (sim) {
mazgch 75:ce6e12067d0c 488 if (type == TYPE_PLUS){
mazgch 75:ce6e12067d0c 489 char s[16];
mazgch 75:ce6e12067d0c 490 if (sscanf(buf, "\r\n+CPIN: %[^\r]\r\n", s) >= 1)
mazgch 75:ce6e12067d0c 491 *sim = (0 == strcmp("READY", s)) ? SIM_READY : SIM_PIN;
mazgch 75:ce6e12067d0c 492 } else if (type == TYPE_ERROR) {
mazgch 75:ce6e12067d0c 493 if (strstr(buf, "+CME ERROR: SIM not inserted"))
mazgch 75:ce6e12067d0c 494 *sim = SIM_MISSING;
mazgch 31:a0bed6c1e05d 495 }
mazgch 31:a0bed6c1e05d 496 }
mazgch 31:a0bed6c1e05d 497 return WAIT;
mazgch 21:c4d64830bf02 498 }
mazgch 21:c4d64830bf02 499
mazgch 26:07be5faf8925 500 int MDMParser::_cbCCID(int type, const char* buf, int len, char* ccid)
mazgch 26:07be5faf8925 501 {
mazgch 26:07be5faf8925 502 if ((type == TYPE_PLUS) && ccid){
mazgch 26:07be5faf8925 503 if (sscanf(buf, "\r\n+CCID: %[^\r]\r\n", ccid) == 1)
mazgch 31:a0bed6c1e05d 504 /*TRACE("Got CCID: %s\r\n", ccid)*/;
mazgch 26:07be5faf8925 505 }
mazgch 26:07be5faf8925 506 return WAIT;
mazgch 26:07be5faf8925 507 }
mazgch 26:07be5faf8925 508
mazgch 75:ce6e12067d0c 509 bool MDMParser::registerNet(NetStatus* status /*= NULL*/, int timeout_ms /*= 180000*/)
mazgch 75:ce6e12067d0c 510 {
mazgch 75:ce6e12067d0c 511 Timer timer;
mazgch 75:ce6e12067d0c 512 timer.start();
mazgch 75:ce6e12067d0c 513 INFO("Modem::register\r\n");
mazgch 75:ce6e12067d0c 514 while (!checkNetStatus(status) && !TIMEOUT(timer, timeout_ms))
mazgch 75:ce6e12067d0c 515 RELAX_MS(1000);
mazgch 79:291df065e345 516 if (_net.csd == REG_DENIED) ERROR("CSD Registration Denied\r\n");
mazgch 79:291df065e345 517 if (_net.psd == REG_DENIED) ERROR("PSD Registration Denied\r\n");
mazgch 79:291df065e345 518 return REG_OK(_net.csd) || REG_OK(_net.psd);
mazgch 75:ce6e12067d0c 519 }
mazgch 75:ce6e12067d0c 520
mazgch 28:4d9509e3b1cf 521 bool MDMParser::checkNetStatus(NetStatus* status /*= NULL*/)
mazgch 21:c4d64830bf02 522 {
mazgch 75:ce6e12067d0c 523 memset(&_net, 0, sizeof(_net));
mazgch 75:ce6e12067d0c 524 _net.lac = 0xFFFF;
mazgch 75:ce6e12067d0c 525 _net.ci = 0xFFFFFFFF;
mazgch 21:c4d64830bf02 526 // check registration
mazgch 21:c4d64830bf02 527 sendFormated("AT+CREG?\r\n");
mazgch 88:135fb4bb7aac 528 waitFinalResp(); // don't fail as service could be not subscribed
mazgch 79:291df065e345 529 if (_dev.dev != DEV_LISA_C200) {
mazgch 79:291df065e345 530 // check PSD registration
mazgch 79:291df065e345 531 sendFormated("AT+CGREG?\r\n");
mazgch 88:135fb4bb7aac 532 waitFinalResp(); // don't fail as service could be not subscribed
mazgch 79:291df065e345 533 }
mazgch 79:291df065e345 534 if (REG_OK(_net.csd) || REG_OK(_net.psd))
mazgch 75:ce6e12067d0c 535 {
mazgch 75:ce6e12067d0c 536 // check modem specific status messages
mazgch 75:ce6e12067d0c 537 if (_dev.dev == DEV_LISA_C200) {
mazgch 75:ce6e12067d0c 538 sendFormated("AT+CSS?\r\n");
mazgch 75:ce6e12067d0c 539 if (RESP_OK != waitFinalResp())
mazgch 75:ce6e12067d0c 540 return false;
mazgch 81:3966a5c17037 541 while (1) {
mazgch 81:3966a5c17037 542 // get the Telephone number
mazgch 81:3966a5c17037 543 sendFormated("AT$MDN?\r\n");
mazgch 81:3966a5c17037 544 if (RESP_OK != waitFinalResp(_cbString, _net.num))
mazgch 81:3966a5c17037 545 return false;
mazgch 81:3966a5c17037 546 // check if we have a Mobile Directory Number
mazgch 81:3966a5c17037 547 if (*_net.num && (memcmp(_net.num, "000000", 6) != 0))
mazgch 81:3966a5c17037 548 break;
mazgch 81:3966a5c17037 549
mazgch 81:3966a5c17037 550 INFO("Device not yet activated\r\n");
mazgch 81:3966a5c17037 551 INFO("Make sure you have a valid contract with the network operator for this device.\r\n");
mazgch 81:3966a5c17037 552 // Check if the the version contains a V for Verizon
mazgch 81:3966a5c17037 553 // Verizon: E0.V.xx.00.xxR,
mazgch 81:3966a5c17037 554 // Sprint E0.S.xx.00.xxR
mazgch 81:3966a5c17037 555 if (_dev.ver[3] == 'V') {
mazgch 81:3966a5c17037 556 int i;
mazgch 81:3966a5c17037 557 INFO("Start device over-the-air activation (this can take a few minutes)\r\n");
mazgch 81:3966a5c17037 558 sendFormated("AT+CDV=*22899\r\n");
mazgch 81:3966a5c17037 559 i = 1;
mazgch 82:055dcfcf9dcc 560 if ((RESP_OK != waitFinalResp(_cbUACTIND, &i, 120*1000)) || (i == 1)) {
mazgch 81:3966a5c17037 561 ERROR("Device over-the-air activation failed\r\n");
mazgch 81:3966a5c17037 562 return false;
mazgch 81:3966a5c17037 563 }
mazgch 81:3966a5c17037 564 INFO("Device over-the-air activation successful\r\n");
mazgch 81:3966a5c17037 565
mazgch 81:3966a5c17037 566 INFO("Start PRL over-the-air update (this can take a few minutes)\r\n");
mazgch 81:3966a5c17037 567 sendFormated("AT+CDV=*22891\r\n");
mazgch 81:3966a5c17037 568 i = 1;
mazgch 82:055dcfcf9dcc 569 if ((RESP_OK != waitFinalResp(_cbUACTIND, &i, 120*1000)) || (i == 1)) {
mazgch 81:3966a5c17037 570 ERROR("PRL over-the-air update failed\r\n");
mazgch 81:3966a5c17037 571 return false;
mazgch 81:3966a5c17037 572 }
mazgch 81:3966a5c17037 573 INFO("PRL over-the-air update successful\r\n");
mazgch 81:3966a5c17037 574
mazgch 81:3966a5c17037 575 } else {
mazgch 81:3966a5c17037 576 // Sprint or Aeris
mazgch 81:3966a5c17037 577 INFO("Wait for OMA-DM over-the-air activation (this can take a few minutes)\r\n");
mazgch 81:3966a5c17037 578 RELAX_MS(120*1000);
mazgch 81:3966a5c17037 579 }
mazgch 81:3966a5c17037 580 }
mazgch 75:ce6e12067d0c 581 // get the the Network access identifier string
mazgch 75:ce6e12067d0c 582 char nai[64];
mazgch 75:ce6e12067d0c 583 sendFormated("AT$QCMIPNAI?\r\n");
mazgch 75:ce6e12067d0c 584 if (RESP_OK != waitFinalResp(_cbString, nai))
mazgch 75:ce6e12067d0c 585 return false;
mazgch 75:ce6e12067d0c 586 } else {
mazgch 75:ce6e12067d0c 587 sendFormated("AT+COPS?\r\n");
mazgch 75:ce6e12067d0c 588 if (RESP_OK != waitFinalResp(_cbCOPS, &_net))
mazgch 75:ce6e12067d0c 589 return false;
mazgch 75:ce6e12067d0c 590 // Returns the MSISDNs related to this subscriber
mazgch 75:ce6e12067d0c 591 sendFormated("AT+CNUM\r\n");
mazgch 75:ce6e12067d0c 592 if (RESP_OK != waitFinalResp(_cbCNUM, _net.num))
mazgch 75:ce6e12067d0c 593 return false;
mazgch 75:ce6e12067d0c 594 }
mazgch 75:ce6e12067d0c 595 // Returns the signal strength indication
mazgch 75:ce6e12067d0c 596 sendFormated("AT+CSQ\r\n");
mazgch 75:ce6e12067d0c 597 if (RESP_OK != waitFinalResp(_cbCSQ, &_net))
mazgch 32:8f12ac182bbb 598 return false;
mazgch 75:ce6e12067d0c 599 }
mazgch 28:4d9509e3b1cf 600 if (status) {
mazgch 31:a0bed6c1e05d 601 memcpy(status, &_net, sizeof(NetStatus));
mazgch 25:4045d02e44f1 602 }
mazgch 79:291df065e345 603 return REG_DONE(_net.csd) && REG_DONE(_net.psd);
mazgch 31:a0bed6c1e05d 604 }
mazgch 31:a0bed6c1e05d 605
mazgch 31:a0bed6c1e05d 606 int MDMParser::_cbCOPS(int type, const char* buf, int len, NetStatus* status)
mazgch 31:a0bed6c1e05d 607 {
mazgch 31:a0bed6c1e05d 608 if ((type == TYPE_PLUS) && status){
mazgch 31:a0bed6c1e05d 609 int act = 99;
mazgch 31:a0bed6c1e05d 610 // +COPS: <mode>[,<format>,<oper>[,<AcT>]]
mazgch 31:a0bed6c1e05d 611 if (sscanf(buf, "\r\n+COPS: %*d,%*d,\"%[^\"]\",%d",status->opr,&act) >= 1) {
mazgch 31:a0bed6c1e05d 612 if (act == 0) status->act = ACT_GSM; // 0: GSM,
mazgch 31:a0bed6c1e05d 613 else if (act == 2) status->act = ACT_UTRAN; // 2: UTRAN
mazgch 31:a0bed6c1e05d 614 }
mazgch 31:a0bed6c1e05d 615 }
mazgch 31:a0bed6c1e05d 616 return WAIT;
mazgch 31:a0bed6c1e05d 617 }
mazgch 31:a0bed6c1e05d 618
mazgch 31:a0bed6c1e05d 619 int MDMParser::_cbCNUM(int type, const char* buf, int len, char* num)
mazgch 31:a0bed6c1e05d 620 {
mazgch 31:a0bed6c1e05d 621 if ((type == TYPE_PLUS) && num){
mazgch 31:a0bed6c1e05d 622 int a;
mazgch 31:a0bed6c1e05d 623 if ((sscanf(buf, "\r\n+CNUM: \"My Number\",\"%31[^\"]\",%d", num, &a) == 2) &&
mazgch 31:a0bed6c1e05d 624 ((a == 129) || (a == 145))) {
mazgch 31:a0bed6c1e05d 625 }
mazgch 31:a0bed6c1e05d 626 }
mazgch 31:a0bed6c1e05d 627 return WAIT;
mazgch 31:a0bed6c1e05d 628 }
mazgch 31:a0bed6c1e05d 629
mazgch 54:7ba8e4c218e2 630 int MDMParser::_cbCSQ(int type, const char* buf, int len, NetStatus* status)
mazgch 31:a0bed6c1e05d 631 {
mazgch 54:7ba8e4c218e2 632 if ((type == TYPE_PLUS) && status){
mazgch 54:7ba8e4c218e2 633 int a,b;
mazgch 54:7ba8e4c218e2 634 char _ber[] = { 49, 43, 37, 25, 19, 13, 7, 0 }; // see 3GPP TS 45.008 [20] subclause 8.2.4
mazgch 31:a0bed6c1e05d 635 // +CSQ: <rssi>,<qual>
mazgch 54:7ba8e4c218e2 636 if (sscanf(buf, "\r\n+CSQ: %d,%d",&a,&b) == 2) {
mazgch 54:7ba8e4c218e2 637 if (a != 99) status->rssi = -113 + 2*a; // 0: -113 1: -111 ... 30: -53 dBm with 2 dBm steps
mazgch 54:7ba8e4c218e2 638 if ((b != 99) && (b < sizeof(_ber))) status->ber = _ber[b]; //
mazgch 31:a0bed6c1e05d 639 }
mazgch 31:a0bed6c1e05d 640 }
mazgch 31:a0bed6c1e05d 641 return WAIT;
mazgch 31:a0bed6c1e05d 642 }
mazgch 21:c4d64830bf02 643
mazgch 81:3966a5c17037 644 int MDMParser::_cbUACTIND(int type, const char* buf, int len, int* i)
mazgch 81:3966a5c17037 645 {
mazgch 81:3966a5c17037 646 if ((type == TYPE_PLUS) && i){
mazgch 82:055dcfcf9dcc 647 int a;
mazgch 82:055dcfcf9dcc 648 if (sscanf(buf, "\r\n+UACTIND: %d", &a) == 1) {
mazgch 82:055dcfcf9dcc 649 *i = a;
mazgch 81:3966a5c17037 650 }
mazgch 81:3966a5c17037 651 }
mazgch 81:3966a5c17037 652 return WAIT;
mazgch 81:3966a5c17037 653 }
mazgch 81:3966a5c17037 654
mazgch 21:c4d64830bf02 655 // ----------------------------------------------------------------
mazgch 21:c4d64830bf02 656 // internet connection
mazgch 21:c4d64830bf02 657
mazgch 79:291df065e345 658 MDMParser::IP MDMParser::join(const char* apn /*= NULL*/, const char* username /*= NULL*/,
mazgch 79:291df065e345 659 const char* password /*= NULL*/, Auth auth /*= AUTH_DETECT*/)
mazgch 21:c4d64830bf02 660 {
mazgch 78:caf0014375cb 661 INFO("Modem::join\r\n");
mazgch 32:8f12ac182bbb 662 _ip = NOIP;
mazgch 32:8f12ac182bbb 663 if (_dev.dev == DEV_LISA_C200) {
mazgch 76:f7c3dd568dae 664 // make a dumy dns lookup (which will fail, so ignore the result)
mazgch 76:f7c3dd568dae 665 sendFormated("AT+UDNSRN=0,\"u-blox.com\"\r\n");
mazgch 76:f7c3dd568dae 666 waitFinalResp();
mazgch 76:f7c3dd568dae 667 // This fake lookup will enable the IP connection and we
mazgch 76:f7c3dd568dae 668 // should have an IP after this, so we check it
mazgch 76:f7c3dd568dae 669
mazgch 21:c4d64830bf02 670 //Get local IP address
mazgch 21:c4d64830bf02 671 sendFormated("AT+CMIP?\r\n");
mazgch 52:8071747a7cb3 672 if (RESP_OK != waitFinalResp(_cbCMIP, &_ip))
mazgch 31:a0bed6c1e05d 673 return NOIP;
mazgch 21:c4d64830bf02 674 } else {
mazgch 21:c4d64830bf02 675 // check gprs attach status
mazgch 72:d1e943ad6558 676 sendFormated("AT+CGATT=1\r\n");
mazgch 74:208e3e32d263 677 if (RESP_OK != waitFinalResp(NULL,NULL,3*60*1000))
mazgch 31:a0bed6c1e05d 678 return NOIP;
mazgch 31:a0bed6c1e05d 679
mazgch 31:a0bed6c1e05d 680 // Check the profile
mazgch 31:a0bed6c1e05d 681 int a = 0;
mazgch 79:291df065e345 682 bool force = true;
mazgch 31:a0bed6c1e05d 683 sendFormated("AT+UPSND=" PROFILE ",8\r\n");
mazgch 52:8071747a7cb3 684 if (RESP_OK != waitFinalResp(_cbUPSND, &a))
mazgch 31:a0bed6c1e05d 685 return NOIP;
mazgch 79:291df065e345 686 if (a == 1 && force) {
mazgch 31:a0bed6c1e05d 687 // disconnect the profile already if it is connected
mazgch 31:a0bed6c1e05d 688 sendFormated("AT+UPSDA=" PROFILE ",4\r\n");
mazgch 58:e38a2e942fbb 689 if (RESP_OK != waitFinalResp(NULL,NULL,40*1000))
mazgch 84:a05edb010176 690 return NOIP;
mazgch 79:291df065e345 691 a = 0;
mazgch 31:a0bed6c1e05d 692 }
mazgch 79:291df065e345 693 if (a == 0) {
mazgch 84:a05edb010176 694 bool ok = false;
mazgch 84:a05edb010176 695 // try to lookup the apn settings from our local database by mccmnc
mazgch 84:a05edb010176 696 const char* config = NULL;
mazgch 88:135fb4bb7aac 697 if (!apn && !username && !password)
mazgch 88:135fb4bb7aac 698 config = apnconfig(_dev.imsi);
mazgch 84:a05edb010176 699
mazgch 79:291df065e345 700 // Set up the dynamic IP address assignment.
mazgch 79:291df065e345 701 sendFormated("AT+UPSD=" PROFILE ",7,\"0.0.0.0\"\r\n");
mazgch 52:8071747a7cb3 702 if (RESP_OK != waitFinalResp())
mazgch 31:a0bed6c1e05d 703 return NOIP;
mazgch 84:a05edb010176 704
mazgch 84:a05edb010176 705 do {
mazgch 84:a05edb010176 706 if (config) {
mazgch 85:dd8f4f0d0ca9 707 apn = _APN_GET(config);
mazgch 85:dd8f4f0d0ca9 708 username = _APN_GET(config);
mazgch 85:dd8f4f0d0ca9 709 password = _APN_GET(config);
mazgch 84:a05edb010176 710 TRACE("Testing APN Settings(\"%s\",\"%s\",\"%s\")\r\n", apn, username, password);
mazgch 79:291df065e345 711 }
mazgch 84:a05edb010176 712 // Set up the APN
mazgch 90:3915192f6d7e 713 if (apn && *apn) {
mazgch 90:3915192f6d7e 714 sendFormated("AT+UPSD=" PROFILE ",1,\"%s\"\r\n", apn);
mazgch 90:3915192f6d7e 715 if (RESP_OK != waitFinalResp())
mazgch 90:3915192f6d7e 716 return NOIP;
mazgch 90:3915192f6d7e 717 }
mazgch 90:3915192f6d7e 718 if (username && *username) {
mazgch 90:3915192f6d7e 719 sendFormated("AT+UPSD=" PROFILE ",2,\"%s\"\r\n", username);
mazgch 90:3915192f6d7e 720 if (RESP_OK != waitFinalResp())
mazgch 90:3915192f6d7e 721 return NOIP;
mazgch 90:3915192f6d7e 722 }
mazgch 90:3915192f6d7e 723 if (password && *password) {
mazgch 90:3915192f6d7e 724 sendFormated("AT+UPSD=" PROFILE ",3,\"%s\"\r\n", password);
mazgch 90:3915192f6d7e 725 if (RESP_OK != waitFinalResp())
mazgch 90:3915192f6d7e 726 return NOIP;
mazgch 90:3915192f6d7e 727 }
mazgch 84:a05edb010176 728 // try different Authentication Protocols
mazgch 84:a05edb010176 729 // 0 = none
mazgch 84:a05edb010176 730 // 1 = PAP (Password Authentication Protocol)
mazgch 84:a05edb010176 731 // 2 = CHAP (Challenge Handshake Authentication Protocol)
mazgch 84:a05edb010176 732 for (int i = AUTH_NONE; i <= AUTH_CHAP && !ok; i ++) {
mazgch 84:a05edb010176 733 if ((auth == AUTH_DETECT) || (auth == i)) {
mazgch 84:a05edb010176 734 // Set up the Authentication Protocol
mazgch 84:a05edb010176 735 sendFormated("AT+UPSD=" PROFILE ",6,%d\r\n", i);
mazgch 84:a05edb010176 736 if (RESP_OK != waitFinalResp())
mazgch 84:a05edb010176 737 return NOIP;
mazgch 84:a05edb010176 738 // Activate the profile and make connection
mazgch 84:a05edb010176 739 sendFormated("AT+UPSDA=" PROFILE ",3\r\n");
mazgch 84:a05edb010176 740 if (RESP_OK == waitFinalResp(NULL,NULL,150*1000))
mazgch 84:a05edb010176 741 ok = true;
mazgch 84:a05edb010176 742 }
mazgch 84:a05edb010176 743 }
mazgch 85:dd8f4f0d0ca9 744 } while (config && *config); // maybe use next setting ?
mazgch 84:a05edb010176 745 if (!ok) {
mazgch 79:291df065e345 746 ERROR("Your modem APN/password/username may be wrong\r\n");
mazgch 31:a0bed6c1e05d 747 return NOIP;
mazgch 79:291df065e345 748 }
mazgch 74:208e3e32d263 749 }
mazgch 21:c4d64830bf02 750 //Get local IP address
mazgch 21:c4d64830bf02 751 sendFormated("AT+UPSND=" PROFILE ",0\r\n");
mazgch 52:8071747a7cb3 752 if (RESP_OK != waitFinalResp(_cbUPSND, &_ip))
mazgch 31:a0bed6c1e05d 753 return NOIP;
mazgch 31:a0bed6c1e05d 754 }
mazgch 32:8f12ac182bbb 755 return _ip;
mazgch 31:a0bed6c1e05d 756 }
mazgch 31:a0bed6c1e05d 757
mazgch 84:a05edb010176 758 int MDMParser::_cbUDOPN(int type, const char* buf, int len, char* mccmnc)
mazgch 84:a05edb010176 759 {
mazgch 84:a05edb010176 760 if ((type == TYPE_PLUS) && mccmnc) {
mazgch 84:a05edb010176 761 if (sscanf(buf, "\r\n+UDOPN: 0,\"%[^\"]\"", mccmnc) == 1)
mazgch 84:a05edb010176 762 ;
mazgch 84:a05edb010176 763 }
mazgch 84:a05edb010176 764 return WAIT;
mazgch 84:a05edb010176 765 }
mazgch 84:a05edb010176 766
mazgch 32:8f12ac182bbb 767 int MDMParser::_cbCMIP(int type, const char* buf, int len, IP* ip)
mazgch 32:8f12ac182bbb 768 {
mazgch 32:8f12ac182bbb 769 if ((type == TYPE_PLUS) && ip) {
mazgch 32:8f12ac182bbb 770 int a,b,c,d;
mazgch 32:8f12ac182bbb 771 if (sscanf(buf, "\r\n+CMIP: " IPSTR, &a,&b,&c,&d) == 4)
mazgch 32:8f12ac182bbb 772 *ip = IPADR(a,b,c,d);
mazgch 32:8f12ac182bbb 773 }
mazgch 32:8f12ac182bbb 774 return WAIT;
mazgch 32:8f12ac182bbb 775 }
mazgch 32:8f12ac182bbb 776
mazgch 31:a0bed6c1e05d 777 int MDMParser::_cbUPSND(int type, const char* buf, int len, int* act)
mazgch 31:a0bed6c1e05d 778 {
mazgch 31:a0bed6c1e05d 779 if ((type == TYPE_PLUS) && act) {
mazgch 31:a0bed6c1e05d 780 if (sscanf(buf, "\r\n+UPSND: %*d,%*d,%d", act) == 1)
mazgch 31:a0bed6c1e05d 781 /*nothing*/;
mazgch 21:c4d64830bf02 782 }
mazgch 31:a0bed6c1e05d 783 return WAIT;
mazgch 31:a0bed6c1e05d 784 }
mazgch 31:a0bed6c1e05d 785
mazgch 31:a0bed6c1e05d 786 int MDMParser::_cbUPSND(int type, const char* buf, int len, IP* ip)
mazgch 31:a0bed6c1e05d 787 {
mazgch 31:a0bed6c1e05d 788 if ((type == TYPE_PLUS) && ip) {
mazgch 31:a0bed6c1e05d 789 int a,b,c,d;
mazgch 31:a0bed6c1e05d 790 // +UPSND=<profile_id>,<param_tag>[,<dynamic_param_val>]
mazgch 31:a0bed6c1e05d 791 if (sscanf(buf, "\r\n+UPSND: " PROFILE ",0,\"" IPSTR "\"", &a,&b,&c,&d) == 4)
mazgch 31:a0bed6c1e05d 792 *ip = IPADR(a,b,c,d);
mazgch 31:a0bed6c1e05d 793 }
mazgch 31:a0bed6c1e05d 794 return WAIT;
mazgch 31:a0bed6c1e05d 795 }
mazgch 31:a0bed6c1e05d 796
mazgch 31:a0bed6c1e05d 797 int MDMParser::_cbUDNSRN(int type, const char* buf, int len, IP* ip)
mazgch 31:a0bed6c1e05d 798 {
mazgch 31:a0bed6c1e05d 799 if ((type == TYPE_PLUS) && ip) {
mazgch 31:a0bed6c1e05d 800 int a,b,c,d;
mazgch 31:a0bed6c1e05d 801 if (sscanf(buf, "\r\n+UDNSRN: \""IPSTR"\"", &a,&b,&c,&d) == 4)
mazgch 31:a0bed6c1e05d 802 *ip = IPADR(a,b,c,d);
mazgch 31:a0bed6c1e05d 803 }
mazgch 31:a0bed6c1e05d 804 return WAIT;
mazgch 21:c4d64830bf02 805 }
mazgch 21:c4d64830bf02 806
mazgch 21:c4d64830bf02 807 bool MDMParser::disconnect(void)
mazgch 21:c4d64830bf02 808 {
mazgch 31:a0bed6c1e05d 809 if (_ip == NOIP)
mazgch 21:c4d64830bf02 810 return true;
mazgch 74:208e3e32d263 811 INFO("Modem::disconnect\r\n");
mazgch 32:8f12ac182bbb 812 if (_dev.dev == DEV_LISA_C200) {
mazgch 76:f7c3dd568dae 813 // There something to do here
mazgch 21:c4d64830bf02 814 } else {
mazgch 21:c4d64830bf02 815 sendFormated("AT+UPSDA=" PROFILE ",4\r\n");
mazgch 52:8071747a7cb3 816 if (RESP_OK != waitFinalResp())
mazgch 32:8f12ac182bbb 817 return false;
mazgch 21:c4d64830bf02 818 }
mazgch 31:a0bed6c1e05d 819 _ip = NOIP;
mazgch 21:c4d64830bf02 820 return true;
mazgch 21:c4d64830bf02 821 }
mazgch 21:c4d64830bf02 822
mazgch 31:a0bed6c1e05d 823 MDMParser::IP MDMParser::gethostbyname(const char* host)
mazgch 21:c4d64830bf02 824 {
mazgch 31:a0bed6c1e05d 825 IP ip = NOIP;
mazgch 31:a0bed6c1e05d 826 int a,b,c,d;
mazgch 31:a0bed6c1e05d 827 if (sscanf(host, IPSTR, &a,&b,&c,&d) == 4)
mazgch 31:a0bed6c1e05d 828 ip = IPADR(a,b,c,d);
mazgch 31:a0bed6c1e05d 829 else {
mazgch 31:a0bed6c1e05d 830 sendFormated("AT+UDNSRN=0,\"%s\"\r\n", host);
mazgch 52:8071747a7cb3 831 if (RESP_OK != waitFinalResp(_cbUDNSRN, &ip))
mazgch 31:a0bed6c1e05d 832 return false;
mazgch 21:c4d64830bf02 833 }
mazgch 31:a0bed6c1e05d 834 return ip;
mazgch 21:c4d64830bf02 835 }
mazgch 21:c4d64830bf02 836
mazgch 21:c4d64830bf02 837 // ----------------------------------------------------------------
mazgch 21:c4d64830bf02 838 // sockets
mazgch 21:c4d64830bf02 839
mazgch 21:c4d64830bf02 840 int MDMParser::_cbUSOCR(int type, const char* buf, int len, int* socket)
mazgch 21:c4d64830bf02 841 {
mazgch 21:c4d64830bf02 842 if ((type == TYPE_PLUS) && socket) {
mazgch 21:c4d64830bf02 843 const char* p = strstr(buf,"+USOCR: ");
mazgch 21:c4d64830bf02 844 if (p)
mazgch 21:c4d64830bf02 845 *socket = atoi(p+8);
mazgch 21:c4d64830bf02 846 }
mazgch 21:c4d64830bf02 847 return WAIT;
mazgch 21:c4d64830bf02 848 }
mazgch 21:c4d64830bf02 849
mazgch 63:42cb563a25bc 850 int MDMParser::socketSocket(IpProtocol ipproto, int port)
mazgch 21:c4d64830bf02 851 {
mazgch 47:9a89e5195721 852 TRACE("socketSocket(%d)\r\n", ipproto);
mazgch 21:c4d64830bf02 853 if(ipproto == IPPROTO_TCP) {
mazgch 63:42cb563a25bc 854 sendFormated("AT+USOCR=6\r\n");
mazgch 63:42cb563a25bc 855 } else if ((ipproto == IPPROTO_UDP) && (port == -1)){
mazgch 63:42cb563a25bc 856 sendFormated("AT+USOCR=17\r\n");
mazgch 63:42cb563a25bc 857 } else if (ipproto == IPPROTO_UDP){
mazgch 63:42cb563a25bc 858 sendFormated("AT+USOCR=17,%d\r\n", port);
mazgch 21:c4d64830bf02 859 } else { // other types not supported
mazgch 21:c4d64830bf02 860 return SOCKET_ERROR;
mazgch 21:c4d64830bf02 861 }
mazgch 21:c4d64830bf02 862 int socket = -1;
mazgch 52:8071747a7cb3 863 if (RESP_OK != waitFinalResp(_cbUSOCR, &socket))
mazgch 21:c4d64830bf02 864 return SOCKET_ERROR;
mazgch 21:c4d64830bf02 865 if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_FREE))
mazgch 21:c4d64830bf02 866 return SOCKET_ERROR;
mazgch 21:c4d64830bf02 867 // successfull
mazgch 21:c4d64830bf02 868 _sockets[socket].state = SOCK_CREATED;
mazgch 21:c4d64830bf02 869 _sockets[socket].pending = 0;
mazgch 66:69072b3c5bca 870 _sockets[socket].timeout_ms = TIMEOUT_BLOCKING;
mazgch 21:c4d64830bf02 871 return socket;
mazgch 21:c4d64830bf02 872 }
mazgch 21:c4d64830bf02 873
mazgch 21:c4d64830bf02 874 bool MDMParser::socketConnect(int socket, const char * host, int port)
mazgch 21:c4d64830bf02 875 {
mazgch 47:9a89e5195721 876 TRACE("socketConnect(%d,%s,%d)\r\n", socket, host,port);
mazgch 31:a0bed6c1e05d 877 IP ip = gethostbyname(host);
mazgch 31:a0bed6c1e05d 878 if (ip == NOIP)
mazgch 21:c4d64830bf02 879 return false;
mazgch 21:c4d64830bf02 880 // connect to socket
mazgch 21:c4d64830bf02 881 if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_CREATED))
mazgch 21:c4d64830bf02 882 return false;
mazgch 21:c4d64830bf02 883 sendFormated("AT+USOCO=%d,\"" IPSTR "\",%d\r\n", socket, IPNUM(ip), port);
mazgch 52:8071747a7cb3 884 if (RESP_OK != waitFinalResp())
mazgch 21:c4d64830bf02 885 return false;
mazgch 21:c4d64830bf02 886 _sockets[socket].state = SOCK_CONNECTED;
mazgch 21:c4d64830bf02 887 return true;
mazgch 21:c4d64830bf02 888 }
mazgch 21:c4d64830bf02 889
mazgch 44:9d12223b78ff 890 bool MDMParser::socketIsConnected(int socket)
mazgch 44:9d12223b78ff 891 {
mazgch 47:9a89e5195721 892 TRACE("socketIsConnected(%d)\r\n", socket);
mazgch 47:9a89e5195721 893 if (!ISSOCKET(socket))
mazgch 47:9a89e5195721 894 return false;
mazgch 47:9a89e5195721 895 return _sockets[socket].state == SOCK_CONNECTED;
mazgch 44:9d12223b78ff 896 }
mazgch 44:9d12223b78ff 897
mazgch 66:69072b3c5bca 898 bool MDMParser::socketSetBlocking(int socket, int timeout_ms)
mazgch 44:9d12223b78ff 899 {
mazgch 47:9a89e5195721 900 TRACE("socketSetBlocking(%d,%d)\r\n", socket, timeout_ms);
mazgch 44:9d12223b78ff 901 if (!ISSOCKET(socket))
mazgch 44:9d12223b78ff 902 return false;
mazgch 44:9d12223b78ff 903 _sockets[socket].timeout_ms = timeout_ms;
mazgch 44:9d12223b78ff 904 return true;
mazgch 44:9d12223b78ff 905 }
mazgch 44:9d12223b78ff 906
mazgch 21:c4d64830bf02 907 bool MDMParser::socketClose(int socket)
mazgch 21:c4d64830bf02 908 {
mazgch 47:9a89e5195721 909 TRACE("socketClose(%d)\r\n", socket);
mazgch 21:c4d64830bf02 910 if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_CONNECTED))
mazgch 21:c4d64830bf02 911 return false;
mazgch 21:c4d64830bf02 912 sendFormated("AT+USOCL=%d\r\n", socket);
mazgch 52:8071747a7cb3 913 if (RESP_OK != waitFinalResp())
mazgch 21:c4d64830bf02 914 return false;
mazgch 44:9d12223b78ff 915 _sockets[socket].state = SOCK_CREATED;
mazgch 21:c4d64830bf02 916 return true;
mazgch 21:c4d64830bf02 917 }
mazgch 21:c4d64830bf02 918
mazgch 21:c4d64830bf02 919 bool MDMParser::socketFree(int socket)
mazgch 21:c4d64830bf02 920 {
mazgch 47:9a89e5195721 921 TRACE("socketFree(%d)\r\n", socket);
mazgch 21:c4d64830bf02 922 socketClose(socket);
mazgch 21:c4d64830bf02 923 if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_CREATED))
mazgch 21:c4d64830bf02 924 return false;
mazgch 21:c4d64830bf02 925 _sockets[socket].state = SOCK_FREE;
mazgch 21:c4d64830bf02 926 return true;
mazgch 21:c4d64830bf02 927 }
mazgch 21:c4d64830bf02 928
mazgch 69:4d6fa520dfca 929 #define USO_MAX_WRITE 1024 //!< maximum number of bytes to write to socket
mazgch 69:4d6fa520dfca 930
mazgch 21:c4d64830bf02 931 int MDMParser::socketSend(int socket, const char * buf, int len)
mazgch 21:c4d64830bf02 932 {
mazgch 47:9a89e5195721 933 TRACE("socketSend(%d,,%d)\r\n", socket,len);
mazgch 68:33a96cf64986 934 int cnt = len;
mazgch 68:33a96cf64986 935 while (cnt > 0) {
mazgch 69:4d6fa520dfca 936 int blk = USO_MAX_WRITE;
mazgch 68:33a96cf64986 937 if (cnt < blk)
mazgch 68:33a96cf64986 938 blk = cnt;
mazgch 68:33a96cf64986 939 sendFormated("AT+USOWR=%d,%d\r\n",socket,blk);
mazgch 52:8071747a7cb3 940 if (RESP_PROMPT != waitFinalResp())
mazgch 21:c4d64830bf02 941 return SOCKET_ERROR;
mazgch 74:208e3e32d263 942 RELAX_MS(50);
mazgch 68:33a96cf64986 943 send(buf, blk);
mazgch 52:8071747a7cb3 944 if (RESP_OK != waitFinalResp())
mazgch 21:c4d64830bf02 945 return SOCKET_ERROR;
mazgch 68:33a96cf64986 946 buf += blk;
mazgch 68:33a96cf64986 947 cnt -= blk;
mazgch 21:c4d64830bf02 948 }
mazgch 68:33a96cf64986 949 return (len - cnt);
mazgch 21:c4d64830bf02 950 }
mazgch 21:c4d64830bf02 951
mazgch 21:c4d64830bf02 952 int MDMParser::socketSendTo(int socket, IP ip, int port, const char * buf, int len)
mazgch 21:c4d64830bf02 953 {
mazgch 65:dd94f920a762 954 TRACE("socketSendTo(%d," IPSTR ",%d,,%d)\r\n", socket, IPNUM(ip),port,len);
mazgch 68:33a96cf64986 955 int cnt = len;
mazgch 68:33a96cf64986 956 while (cnt > 0) {
mazgch 69:4d6fa520dfca 957 int blk = USO_MAX_WRITE;
mazgch 68:33a96cf64986 958 if (cnt < blk)
mazgch 68:33a96cf64986 959 blk = cnt;
mazgch 68:33a96cf64986 960 sendFormated("AT+USOST=%d,\"" IPSTR "\",%d,%d\r\n",socket,IPNUM(ip),port,blk);
mazgch 52:8071747a7cb3 961 if (RESP_PROMPT != waitFinalResp())
mazgch 21:c4d64830bf02 962 return SOCKET_ERROR;
mazgch 74:208e3e32d263 963 RELAX_MS(50);
mazgch 68:33a96cf64986 964 send(buf, blk);
mazgch 63:42cb563a25bc 965 if (RESP_OK != waitFinalResp())
mazgch 21:c4d64830bf02 966 return SOCKET_ERROR;
mazgch 68:33a96cf64986 967 buf += blk;
mazgch 68:33a96cf64986 968 cnt -= blk;
mazgch 21:c4d64830bf02 969 }
mazgch 68:33a96cf64986 970 return (len - cnt);
mazgch 21:c4d64830bf02 971 }
mazgch 21:c4d64830bf02 972
mazgch 21:c4d64830bf02 973 int MDMParser::socketReadable(int socket)
mazgch 21:c4d64830bf02 974 {
mazgch 47:9a89e5195721 975 TRACE("socketReadable(%d)\r\n", socket);
mazgch 21:c4d64830bf02 976 if (!ISSOCKET(socket) || (_sockets[socket].state != SOCK_CONNECTED))
mazgch 21:c4d64830bf02 977 return SOCKET_ERROR;
mazgch 21:c4d64830bf02 978 // allow to receive unsolicited commands
mazgch 21:c4d64830bf02 979 waitFinalResp(NULL, NULL, 0);
mazgch 21:c4d64830bf02 980 if (_sockets[socket].state != SOCK_CONNECTED)
mazgch 21:c4d64830bf02 981 return SOCKET_ERROR;
mazgch 21:c4d64830bf02 982 return _sockets[socket].pending;
mazgch 21:c4d64830bf02 983 }
mazgch 21:c4d64830bf02 984
mazgch 21:c4d64830bf02 985 int MDMParser::_cbUSORD(int type, const char* buf, int len, char* out)
mazgch 21:c4d64830bf02 986 {
mazgch 21:c4d64830bf02 987 if ((type == TYPE_PLUS) && out) {
mazgch 21:c4d64830bf02 988 int sz, sk;
mazgch 21:c4d64830bf02 989 if ((sscanf(buf, "\r\n+USORD: %d,%d,", &sk, &sz) == 2) &&
mazgch 21:c4d64830bf02 990 (buf[len-sz-2] == '\"') && (buf[len-1] == '\"')) {
mazgch 21:c4d64830bf02 991 memcpy(out, &buf[len-1-sz], sz);
mazgch 21:c4d64830bf02 992 }
mazgch 21:c4d64830bf02 993 }
mazgch 21:c4d64830bf02 994 return WAIT;
mazgch 21:c4d64830bf02 995 }
mazgch 21:c4d64830bf02 996
mazgch 21:c4d64830bf02 997 int MDMParser::socketRecv(int socket, char* buf, int len)
mazgch 21:c4d64830bf02 998 {
mazgch 21:c4d64830bf02 999 int cnt = 0;
mazgch 47:9a89e5195721 1000 TRACE("socketRecv(%d,,%d)\r\n", socket, len);
mazgch 21:c4d64830bf02 1001 if (!ISSOCKET(socket))
mazgch 21:c4d64830bf02 1002 return SOCKET_ERROR;
mazgch 21:c4d64830bf02 1003 memset(buf, '\0', len);
mazgch 44:9d12223b78ff 1004 Timer timer;
mazgch 44:9d12223b78ff 1005 timer.start();
mazgch 21:c4d64830bf02 1006 while (len) {
mazgch 69:4d6fa520dfca 1007 int blk = MAX_SIZE; // still need space for headers and unsolicited commands
mazgch 21:c4d64830bf02 1008 if (_sockets[socket].pending < blk)
mazgch 21:c4d64830bf02 1009 blk = _sockets[socket].pending;
mazgch 21:c4d64830bf02 1010 if (len < blk) blk = len;
mazgch 21:c4d64830bf02 1011 if (blk) {
mazgch 44:9d12223b78ff 1012 sendFormated("AT+USORD=%d,%d\r\n",socket, blk);
mazgch 52:8071747a7cb3 1013 if (RESP_OK != waitFinalResp(_cbUSORD, buf)) {
mazgch 44:9d12223b78ff 1014 return SOCKET_ERROR;
mazgch 21:c4d64830bf02 1015 }
mazgch 21:c4d64830bf02 1016 len -= blk;
mazgch 21:c4d64830bf02 1017 cnt += blk;
mazgch 21:c4d64830bf02 1018 buf += blk;
mazgch 21:c4d64830bf02 1019 _sockets[socket].pending -= blk;
mazgch 75:ce6e12067d0c 1020 } else if ((_sockets[socket].state == SOCK_CONNECTED) &&
mazgch 75:ce6e12067d0c 1021 !TIMEOUT(timer, _sockets[socket].timeout_ms)) {
mazgch 21:c4d64830bf02 1022 // allow to receive unsolicited commands
mazgch 21:c4d64830bf02 1023 waitFinalResp(NULL, NULL, 10);
mazgch 44:9d12223b78ff 1024 } else {
mazgch 44:9d12223b78ff 1025 len = 0; // no more data and socket closed or timed-out
mazgch 21:c4d64830bf02 1026 }
mazgch 21:c4d64830bf02 1027 }
mazgch 21:c4d64830bf02 1028 return cnt;
mazgch 21:c4d64830bf02 1029 }
mazgch 21:c4d64830bf02 1030
mazgch 21:c4d64830bf02 1031 int MDMParser::_cbUSORF(int type, const char* buf, int len, USORFparam* param)
mazgch 21:c4d64830bf02 1032 {
mazgch 21:c4d64830bf02 1033 if ((type == TYPE_PLUS) && param) {
mazgch 21:c4d64830bf02 1034 int sz, sk, p, a,b,c,d;
mazgch 21:c4d64830bf02 1035 if ((sscanf(buf, "\r\n+USORF: %d,\""IPSTR"\",%d,%d,",
mazgch 21:c4d64830bf02 1036 &sk,&a,&b,&c,&d,&p,&sz) == 7) &&
mazgch 21:c4d64830bf02 1037 (buf[len-sz-2] == '\"') && (buf[len-1] == '\"')) {
mazgch 21:c4d64830bf02 1038 memcpy(param->buf, &buf[len-1-sz], sz);
mazgch 21:c4d64830bf02 1039 param->ip = IPADR(a,b,c,d);
mazgch 21:c4d64830bf02 1040 param->port = p;
mazgch 21:c4d64830bf02 1041 }
mazgch 21:c4d64830bf02 1042 }
mazgch 21:c4d64830bf02 1043 return WAIT;
mazgch 21:c4d64830bf02 1044 }
mazgch 21:c4d64830bf02 1045
mazgch 63:42cb563a25bc 1046 int MDMParser::socketRecvFrom(int socket, IP* ip, int* port, char* buf, int len)
mazgch 21:c4d64830bf02 1047 {
mazgch 21:c4d64830bf02 1048 int cnt = 0;
mazgch 63:42cb563a25bc 1049 TRACE("socketRecvFrom(%d,,%d)\r\n", socket, len);
mazgch 21:c4d64830bf02 1050 if (!ISSOCKET(socket))
mazgch 21:c4d64830bf02 1051 return SOCKET_ERROR;
mazgch 21:c4d64830bf02 1052 memset(buf, '\0', len);
mazgch 44:9d12223b78ff 1053 Timer timer;
mazgch 44:9d12223b78ff 1054 timer.start();
mazgch 21:c4d64830bf02 1055 while (len) {
mazgch 69:4d6fa520dfca 1056 int blk = MAX_SIZE; // still need space for headers and unsolicited commands
mazgch 21:c4d64830bf02 1057 if (_sockets[socket].pending < blk)
mazgch 21:c4d64830bf02 1058 blk = _sockets[socket].pending;
mazgch 21:c4d64830bf02 1059 if (len < blk) blk = len;
mazgch 21:c4d64830bf02 1060 if (blk) {
mazgch 21:c4d64830bf02 1061 sendFormated("AT+USORF=%d,%d\r\n",socket, blk);
mazgch 21:c4d64830bf02 1062 USORFparam param;
mazgch 21:c4d64830bf02 1063 param.buf = buf;
mazgch 52:8071747a7cb3 1064 if (RESP_OK != waitFinalResp(_cbUSORF, &param)) {
mazgch 44:9d12223b78ff 1065 return SOCKET_ERROR;
mazgch 21:c4d64830bf02 1066 }
mazgch 21:c4d64830bf02 1067 *ip = param.ip;
mazgch 63:42cb563a25bc 1068 *port = param.port;
mazgch 21:c4d64830bf02 1069 len -= blk;
mazgch 21:c4d64830bf02 1070 cnt += blk;
mazgch 21:c4d64830bf02 1071 buf += blk;
mazgch 21:c4d64830bf02 1072 _sockets[socket].pending -= blk;
mazgch 75:ce6e12067d0c 1073 } else if (!TIMEOUT(timer, _sockets[socket].timeout_ms)) {
mazgch 21:c4d64830bf02 1074 // allow to receive unsolicited commands
mazgch 21:c4d64830bf02 1075 waitFinalResp(NULL, NULL, 10);
mazgch 75:ce6e12067d0c 1076 } else
mazgch 44:9d12223b78ff 1077 len = 0; // no more data and socket closed or timed-out
mazgch 21:c4d64830bf02 1078 }
mazgch 44:9d12223b78ff 1079 timer.stop();
mazgch 44:9d12223b78ff 1080 timer.reset();
mazgch 21:c4d64830bf02 1081 return cnt;
mazgch 21:c4d64830bf02 1082 }
mazgch 21:c4d64830bf02 1083
mazgch 21:c4d64830bf02 1084 // ----------------------------------------------------------------
mazgch 31:a0bed6c1e05d 1085
mazgch 31:a0bed6c1e05d 1086 int MDMParser::_cbCMGL(int type, const char* buf, int len, CMGLparam* param)
mazgch 21:c4d64830bf02 1087 {
mazgch 31:a0bed6c1e05d 1088 if ((type == TYPE_PLUS) && param && param->num) {
mazgch 31:a0bed6c1e05d 1089 // +CMGL: <ix>,...
mazgch 31:a0bed6c1e05d 1090 int ix;
mazgch 31:a0bed6c1e05d 1091 if (sscanf(buf, "\r\n+CMGL: %d,", &ix) == 1)
mazgch 31:a0bed6c1e05d 1092 {
mazgch 31:a0bed6c1e05d 1093 *param->ix++ = ix;
mazgch 31:a0bed6c1e05d 1094 param->num--;
mazgch 31:a0bed6c1e05d 1095 }
mazgch 29:53d346010624 1096 }
mazgch 29:53d346010624 1097 return WAIT;
mazgch 21:c4d64830bf02 1098 }
mazgch 21:c4d64830bf02 1099
mazgch 31:a0bed6c1e05d 1100 int MDMParser::smsList(const char* stat /*= "ALL"*/, int* ix /*=NULL*/, int num /*= 0*/) {
mazgch 31:a0bed6c1e05d 1101 sendFormated("AT+CMGL=\"%s\"\r\n", stat);
mazgch 31:a0bed6c1e05d 1102 CMGLparam param;
mazgch 31:a0bed6c1e05d 1103 param.ix = ix;
mazgch 31:a0bed6c1e05d 1104 param.num = num;
mazgch 52:8071747a7cb3 1105 if (RESP_OK != waitFinalResp(_cbCMGL, &param))
mazgch 31:a0bed6c1e05d 1106 return -1;
mazgch 31:a0bed6c1e05d 1107 return num - param.num;
mazgch 21:c4d64830bf02 1108 }
mazgch 21:c4d64830bf02 1109
mazgch 21:c4d64830bf02 1110 bool MDMParser::smsSend(const char* num, const char* buf)
mazgch 21:c4d64830bf02 1111 {
mazgch 80:34985b4d821e 1112 sendFormated("AT+CMGS=\"%s\"\r\n",num);
mazgch 58:e38a2e942fbb 1113 if (RESP_PROMPT != waitFinalResp(NULL,NULL,150*1000)) {
mazgch 21:c4d64830bf02 1114 return false;
mazgch 21:c4d64830bf02 1115 }
mazgch 21:c4d64830bf02 1116 send(buf, strlen(buf));
mazgch 21:c4d64830bf02 1117 const char ctrlZ = 0x1A;
mazgch 21:c4d64830bf02 1118 send(&ctrlZ, sizeof(ctrlZ));
mazgch 52:8071747a7cb3 1119 if (RESP_OK != waitFinalResp()) {
mazgch 21:c4d64830bf02 1120 return false;
mazgch 21:c4d64830bf02 1121 }
mazgch 21:c4d64830bf02 1122 return true;
mazgch 21:c4d64830bf02 1123 }
mazgch 21:c4d64830bf02 1124
mazgch 21:c4d64830bf02 1125 bool MDMParser::smsDelete(int ix)
mazgch 21:c4d64830bf02 1126 {
mazgch 21:c4d64830bf02 1127 sendFormated("AT+CMGD=%d\r\n",ix);
mazgch 52:8071747a7cb3 1128 if (RESP_OK != waitFinalResp()) {
mazgch 21:c4d64830bf02 1129 return false;
mazgch 21:c4d64830bf02 1130 }
mazgch 21:c4d64830bf02 1131 return true;
mazgch 21:c4d64830bf02 1132 }
mazgch 21:c4d64830bf02 1133
mazgch 21:c4d64830bf02 1134 int MDMParser::_cbCMGR(int type, const char* buf, int len, CMGRparam* param)
mazgch 21:c4d64830bf02 1135 {
mazgch 21:c4d64830bf02 1136 if (param) {
mazgch 21:c4d64830bf02 1137 if (type == TYPE_PLUS) {
mazgch 21:c4d64830bf02 1138 if (sscanf(buf, "\r\n+CMGR: \"%*[^\"]\",\"%[^\"]", param->num) == 1) {
mazgch 21:c4d64830bf02 1139 }
mazgch 37:cc3433329d66 1140 } else if ((type == TYPE_UNKNOWN) && (buf[len-2] == '\r') && (buf[len-1] == '\n')) {
mazgch 21:c4d64830bf02 1141 memcpy(param->buf, buf, len-2);
mazgch 21:c4d64830bf02 1142 param->buf[len-2] = '\0';
mazgch 21:c4d64830bf02 1143 }
mazgch 21:c4d64830bf02 1144 }
mazgch 21:c4d64830bf02 1145 return WAIT;
mazgch 21:c4d64830bf02 1146 }
mazgch 21:c4d64830bf02 1147
mazgch 21:c4d64830bf02 1148 bool MDMParser::smsRead(int ix, char* num, char* buf, int len)
mazgch 21:c4d64830bf02 1149 {
mazgch 21:c4d64830bf02 1150 CMGRparam param;
mazgch 21:c4d64830bf02 1151 param.num = num;
mazgch 21:c4d64830bf02 1152 param.buf = buf;
mazgch 21:c4d64830bf02 1153 sendFormated("AT+CMGR=%d\r\n",ix);
mazgch 52:8071747a7cb3 1154 if (RESP_OK != waitFinalResp(_cbCMGR, &param)) {
mazgch 21:c4d64830bf02 1155 return false;
mazgch 21:c4d64830bf02 1156 }
mazgch 21:c4d64830bf02 1157 return true;
mazgch 21:c4d64830bf02 1158 }
mazgch 54:7ba8e4c218e2 1159
mazgch 54:7ba8e4c218e2 1160 // ----------------------------------------------------------------
mazgch 70:0a87d256cd24 1161
mazgch 70:0a87d256cd24 1162 int MDMParser::_cbCUSD(int type, const char* buf, int len, char* resp)
mazgch 70:0a87d256cd24 1163 {
mazgch 70:0a87d256cd24 1164 if ((type == TYPE_PLUS) && resp) {
mazgch 70:0a87d256cd24 1165 // +USD: \"%*[^\"]\",\"%[^\"]\",,\"%*[^\"]\",%d,%d,%d,%d,\"*[^\"]\",%d,%d"..);
mazgch 70:0a87d256cd24 1166 if (sscanf(buf, "\r\n+CUSD: %*d,\"%[^\"]\",%*d", resp) == 1) {
mazgch 70:0a87d256cd24 1167 /*nothing*/
mazgch 70:0a87d256cd24 1168 }
mazgch 70:0a87d256cd24 1169 }
mazgch 70:0a87d256cd24 1170 return WAIT;
mazgch 70:0a87d256cd24 1171 }
mazgch 70:0a87d256cd24 1172
mazgch 70:0a87d256cd24 1173 bool MDMParser::ussdCommand(const char* cmd, char* buf)
mazgch 70:0a87d256cd24 1174 {
mazgch 80:34985b4d821e 1175 if (_dev.dev == DEV_LISA_C200)
mazgch 80:34985b4d821e 1176 return false;
mazgch 70:0a87d256cd24 1177 *buf = '\0';
mazgch 70:0a87d256cd24 1178 sendFormated("AT+CUSD=1,\"%s\"\r\n",cmd);
mazgch 70:0a87d256cd24 1179 if (RESP_OK != waitFinalResp(_cbCUSD, buf)) {
mazgch 70:0a87d256cd24 1180 return false;
mazgch 70:0a87d256cd24 1181 }
mazgch 70:0a87d256cd24 1182 return true;
mazgch 70:0a87d256cd24 1183 }
mazgch 80:34985b4d821e 1184
mazgch 80:34985b4d821e 1185 // ----------------------------------------------------------------
mazgch 70:0a87d256cd24 1186
mazgch 80:34985b4d821e 1187 bool MDMParser::delFile(const char* filename)
mazgch 80:34985b4d821e 1188 {
mazgch 80:34985b4d821e 1189 sendFormated("AT+UDELFILE=\"%s\"\r\n", filename);
mazgch 80:34985b4d821e 1190 if (RESP_OK != waitFinalResp())
mazgch 80:34985b4d821e 1191 return false;
mazgch 80:34985b4d821e 1192 return true;
mazgch 80:34985b4d821e 1193 }
mazgch 80:34985b4d821e 1194
mazgch 80:34985b4d821e 1195 int MDMParser::writeFile(const char* filename, const char* buf, int len)
mazgch 80:34985b4d821e 1196 {
mazgch 80:34985b4d821e 1197 sendFormated("AT+UDWNFILE=\"%s\",%d\r\n", filename, len);
mazgch 80:34985b4d821e 1198 if (RESP_PROMPT != waitFinalResp())
mazgch 80:34985b4d821e 1199 return 0;
mazgch 80:34985b4d821e 1200 send(buf, len);
mazgch 80:34985b4d821e 1201 if (RESP_OK != waitFinalResp())
mazgch 80:34985b4d821e 1202 return 0;
mazgch 80:34985b4d821e 1203 return len;
mazgch 80:34985b4d821e 1204 }
mazgch 80:34985b4d821e 1205
mazgch 80:34985b4d821e 1206 int MDMParser::readFile(const char* filename, char* buf, int len)
mazgch 80:34985b4d821e 1207 {
mazgch 80:34985b4d821e 1208 sendFormated("AT+URDFILE=\"%s\"\r\n", filename, len);
mazgch 80:34985b4d821e 1209 URDFILEparam param;
mazgch 80:34985b4d821e 1210 param.filename = filename;
mazgch 80:34985b4d821e 1211 param.buf = buf;
mazgch 80:34985b4d821e 1212 param.sz = len;
mazgch 80:34985b4d821e 1213 param.len = 0;
mazgch 80:34985b4d821e 1214 if (RESP_OK != waitFinalResp(_cbURDFILE, &param))
mazgch 80:34985b4d821e 1215 return -1;
mazgch 80:34985b4d821e 1216 return param.len;
mazgch 80:34985b4d821e 1217 }
mazgch 80:34985b4d821e 1218
mazgch 80:34985b4d821e 1219 int MDMParser::_cbURDFILE(int type, const char* buf, int len, URDFILEparam* param)
mazgch 80:34985b4d821e 1220 {
mazgch 80:34985b4d821e 1221 if ((type == TYPE_PLUS) && param && param->filename && param->buf) {
mazgch 80:34985b4d821e 1222 char filename[48];
mazgch 80:34985b4d821e 1223 int sz;
mazgch 80:34985b4d821e 1224 if ((sscanf(buf, "\r\n+URDFILE: \"%[^\"]\",%d,", filename, &sz) == 2) &&
mazgch 80:34985b4d821e 1225 (0 == strcmp(param->filename, filename)) &&
mazgch 80:34985b4d821e 1226 (buf[len-sz-2] == '\"') && (buf[len-1] == '\"')) {
mazgch 80:34985b4d821e 1227 param->len = (sz < param->sz) ? sz : param->sz;
mazgch 80:34985b4d821e 1228 memcpy(param->buf, &buf[len-1-sz], param->len);
mazgch 80:34985b4d821e 1229 }
mazgch 80:34985b4d821e 1230 }
mazgch 80:34985b4d821e 1231 return WAIT;
mazgch 80:34985b4d821e 1232 }
mazgch 80:34985b4d821e 1233
mazgch 70:0a87d256cd24 1234 // ----------------------------------------------------------------
mazgch 74:208e3e32d263 1235 bool MDMParser::setDebug(int level)
mazgch 74:208e3e32d263 1236 {
mazgch 74:208e3e32d263 1237 #ifdef MDM_DEBUG
mazgch 74:208e3e32d263 1238 if ((_debugLevel >= 0) && (level >= 0)) {
mazgch 74:208e3e32d263 1239 _debugLevel = level;
mazgch 74:208e3e32d263 1240 return true;
mazgch 74:208e3e32d263 1241 }
mazgch 74:208e3e32d263 1242 #endif
mazgch 74:208e3e32d263 1243 return false;
mazgch 74:208e3e32d263 1244 }
mazgch 74:208e3e32d263 1245
mazgch 73:2b32e0a21df2 1246 void MDMParser::dumpDevStatus(MDMParser::DevStatus* status,
mazgch 73:2b32e0a21df2 1247 _DPRINT dprint, void* param)
mazgch 54:7ba8e4c218e2 1248 {
mazgch 75:ce6e12067d0c 1249 dprint(param, "Modem::devStatus\r\n");
mazgch 54:7ba8e4c218e2 1250 const char* txtDev[] = { "Unknown", "SARA-G350", "LISA-U200", "LISA-C200" };
mazgch 54:7ba8e4c218e2 1251 if (status->dev < sizeof(txtDev)/sizeof(*txtDev) && (status->dev != MDMParser::DEV_UNKNOWN))
mazgch 73:2b32e0a21df2 1252 dprint(param, " Device: %s\r\n", txtDev[status->dev]);
mazgch 54:7ba8e4c218e2 1253 const char* txtLpm[] = { "Disabled", "Enabled", "Active" };
mazgch 54:7ba8e4c218e2 1254 if (status->lpm < sizeof(txtLpm)/sizeof(*txtLpm))
mazgch 73:2b32e0a21df2 1255 dprint(param, " Power Save: %s\r\n", txtLpm[status->lpm]);
mazgch 75:ce6e12067d0c 1256 const char* txtSim[] = { "Unknown", "Missing", "Pin", "Ready" };
mazgch 54:7ba8e4c218e2 1257 if (status->sim < sizeof(txtSim)/sizeof(*txtSim) && (status->sim != MDMParser::SIM_UNKNOWN))
mazgch 73:2b32e0a21df2 1258 dprint(param, " SIM: %s\r\n", txtSim[status->sim]);
mazgch 54:7ba8e4c218e2 1259 if (*status->ccid)
mazgch 73:2b32e0a21df2 1260 dprint(param, " CCID: %s\r\n", status->ccid);
mazgch 54:7ba8e4c218e2 1261 if (*status->imei)
mazgch 73:2b32e0a21df2 1262 dprint(param, " IMEI: %s\r\n", status->imei);
mazgch 54:7ba8e4c218e2 1263 if (*status->imsi)
mazgch 73:2b32e0a21df2 1264 dprint(param, " IMSI: %s\r\n", status->imsi);
mazgch 54:7ba8e4c218e2 1265 if (*status->meid)
mazgch 73:2b32e0a21df2 1266 dprint(param, " MEID: %s\r\n", status->meid); // LISA-C
mazgch 54:7ba8e4c218e2 1267 if (*status->manu)
mazgch 73:2b32e0a21df2 1268 dprint(param, " Manufacturer: %s\r\n", status->manu);
mazgch 54:7ba8e4c218e2 1269 if (*status->model)
mazgch 73:2b32e0a21df2 1270 dprint(param, " Model: %s\r\n", status->model);
mazgch 54:7ba8e4c218e2 1271 if (*status->ver)
mazgch 73:2b32e0a21df2 1272 dprint(param, " Version: %s\r\n", status->ver);
mazgch 54:7ba8e4c218e2 1273 }
mazgch 54:7ba8e4c218e2 1274
mazgch 73:2b32e0a21df2 1275 void MDMParser::dumpNetStatus(MDMParser::NetStatus *status,
mazgch 73:2b32e0a21df2 1276 _DPRINT dprint, void* param)
mazgch 54:7ba8e4c218e2 1277 {
mazgch 75:ce6e12067d0c 1278 dprint(param, "Modem::netStatus\r\n");
mazgch 54:7ba8e4c218e2 1279 const char* txtReg[] = { "Unknown", "Denied", "None", "Home", "Roaming" };
mazgch 79:291df065e345 1280 if (status->csd < sizeof(txtReg)/sizeof(*txtReg) && (status->csd != MDMParser::REG_UNKNOWN))
mazgch 79:291df065e345 1281 dprint(param, " CSD Registration: %s\r\n", txtReg[status->csd]);
mazgch 79:291df065e345 1282 if (status->psd < sizeof(txtReg)/sizeof(*txtReg) && (status->psd != MDMParser::REG_UNKNOWN))
mazgch 79:291df065e345 1283 dprint(param, " PSD Registration: %s\r\n", txtReg[status->psd]);
mazgch 54:7ba8e4c218e2 1284 const char* txtAct[] = { "Unknown", "GSM", "Edge", "3G", "CDMA" };
mazgch 54:7ba8e4c218e2 1285 if (status->act < sizeof(txtAct)/sizeof(*txtAct) && (status->act != MDMParser::ACT_UNKNOWN))
mazgch 73:2b32e0a21df2 1286 dprint(param, " Access Technology: %s\r\n", txtAct[status->act]);
mazgch 54:7ba8e4c218e2 1287 if (status->rssi)
mazgch 73:2b32e0a21df2 1288 dprint(param, " Signal Strength: %d dBm\r\n", status->rssi);
mazgch 54:7ba8e4c218e2 1289 if (status->ber)
mazgch 73:2b32e0a21df2 1290 dprint(param, " Bit Error Rate: %d\r\n", status->ber);
mazgch 54:7ba8e4c218e2 1291 if (*status->opr)
mazgch 73:2b32e0a21df2 1292 dprint(param, " Operator: %s\r\n", status->opr);
mazgch 54:7ba8e4c218e2 1293 if (status->lac != 0xFFFF)
mazgch 73:2b32e0a21df2 1294 dprint(param, " Location Area Code: %04X\r\n", status->lac);
mazgch 54:7ba8e4c218e2 1295 if (status->ci != 0xFFFFFFFF)
mazgch 73:2b32e0a21df2 1296 dprint(param, " Cell ID: %08X\r\n", status->ci);
mazgch 54:7ba8e4c218e2 1297 if (*status->num)
mazgch 73:2b32e0a21df2 1298 dprint(param, " Phone Number: %s\r\n", status->num);
mazgch 54:7ba8e4c218e2 1299 }
mazgch 54:7ba8e4c218e2 1300
mazgch 73:2b32e0a21df2 1301 void MDMParser::dumpIp(MDMParser::IP ip,
mazgch 73:2b32e0a21df2 1302 _DPRINT dprint, void* param)
mazgch 54:7ba8e4c218e2 1303 {
mazgch 57:869bd35f44cc 1304 if (ip != NOIP)
mazgch 75:ce6e12067d0c 1305 dprint(param, "Modem:IP " IPSTR "\r\n", IPNUM(ip));
mazgch 54:7ba8e4c218e2 1306 }
mazgch 70:0a87d256cd24 1307
mazgch 21:c4d64830bf02 1308 // ----------------------------------------------------------------
mazgch 21:c4d64830bf02 1309 int MDMParser::_parseMatch(Pipe<char>* pipe, int len, const char* sta, const char* end)
mazgch 18:e5697801df29 1310 {
mazgch 18:e5697801df29 1311 int o = 0;
mazgch 21:c4d64830bf02 1312 if (sta) {
mazgch 21:c4d64830bf02 1313 while (*sta) {
mazgch 21:c4d64830bf02 1314 if (++o > len) return WAIT;
mazgch 21:c4d64830bf02 1315 char ch = pipe->next();
mazgch 21:c4d64830bf02 1316 if (*sta++ != ch) return NOT_FOUND;
mazgch 21:c4d64830bf02 1317 }
mazgch 21:c4d64830bf02 1318 }
mazgch 21:c4d64830bf02 1319 if (!end) return o; // no termination
mazgch 35:9275215a3a5b 1320 // at least any char
mazgch 35:9275215a3a5b 1321 if (++o > len) return WAIT;
mazgch 35:9275215a3a5b 1322 pipe->next();
mazgch 35:9275215a3a5b 1323 // check the end
mazgch 21:c4d64830bf02 1324 int x = 0;
mazgch 21:c4d64830bf02 1325 while (end[x]) {
mazgch 21:c4d64830bf02 1326 if (++o > len) return WAIT;
mazgch 21:c4d64830bf02 1327 char ch = pipe->next();
mazgch 21:c4d64830bf02 1328 x = (end[x] == ch) ? x + 1 :
mazgch 21:c4d64830bf02 1329 (end[0] == ch) ? 1 :
mazgch 21:c4d64830bf02 1330 0;
mazgch 21:c4d64830bf02 1331 }
mazgch 21:c4d64830bf02 1332 return o;
mazgch 21:c4d64830bf02 1333 }
mazgch 21:c4d64830bf02 1334
mazgch 21:c4d64830bf02 1335 int MDMParser::_parseFormated(Pipe<char>* pipe, int len, const char* fmt)
mazgch 21:c4d64830bf02 1336 {
mazgch 21:c4d64830bf02 1337 int o = 0;
mazgch 21:c4d64830bf02 1338 int num = 0;
mazgch 21:c4d64830bf02 1339 if (fmt) {
mazgch 21:c4d64830bf02 1340 while (*fmt) {
mazgch 21:c4d64830bf02 1341 if (++o > len) return WAIT;
mazgch 21:c4d64830bf02 1342 char ch = pipe->next();
mazgch 21:c4d64830bf02 1343 if (*fmt == '%') {
mazgch 21:c4d64830bf02 1344 fmt++;
mazgch 21:c4d64830bf02 1345 if (*fmt == 'd') { // numeric
mazgch 21:c4d64830bf02 1346 fmt ++;
mazgch 21:c4d64830bf02 1347 num = 0;
mazgch 21:c4d64830bf02 1348 while (ch >= '0' && ch <= '9') {
mazgch 21:c4d64830bf02 1349 num = num * 10 + (ch - '0');
mazgch 21:c4d64830bf02 1350 if (++o > len) return WAIT;
mazgch 21:c4d64830bf02 1351 ch = pipe->next();
mazgch 21:c4d64830bf02 1352 }
mazgch 21:c4d64830bf02 1353 }
mazgch 21:c4d64830bf02 1354 else if (*fmt == 'c') { // char buffer (takes last numeric as length)
mazgch 21:c4d64830bf02 1355 fmt ++;
mazgch 21:c4d64830bf02 1356 while (num --) {
mazgch 21:c4d64830bf02 1357 if (++o > len) return WAIT;
mazgch 21:c4d64830bf02 1358 ch = pipe->next();
mazgch 21:c4d64830bf02 1359 }
mazgch 21:c4d64830bf02 1360 }
mazgch 80:34985b4d821e 1361 else if (*fmt == 's') {
mazgch 80:34985b4d821e 1362 fmt ++;
mazgch 80:34985b4d821e 1363 if (ch != '\"') return NOT_FOUND;
mazgch 80:34985b4d821e 1364 do {
mazgch 80:34985b4d821e 1365 if (++o > len) return WAIT;
mazgch 80:34985b4d821e 1366 ch = pipe->next();
mazgch 80:34985b4d821e 1367 } while (ch != '\"');
mazgch 80:34985b4d821e 1368 if (++o > len) return WAIT;
mazgch 80:34985b4d821e 1369 ch = pipe->next();
mazgch 80:34985b4d821e 1370 }
mazgch 21:c4d64830bf02 1371 }
mazgch 21:c4d64830bf02 1372 if (*fmt++ != ch) return NOT_FOUND;
mazgch 18:e5697801df29 1373 }
mazgch 18:e5697801df29 1374 }
mazgch 21:c4d64830bf02 1375 return o;
mazgch 21:c4d64830bf02 1376 }
mazgch 21:c4d64830bf02 1377
mazgch 21:c4d64830bf02 1378 int MDMParser::_getLine(Pipe<char>* pipe, char* buf, int len)
mazgch 21:c4d64830bf02 1379 {
mazgch 21:c4d64830bf02 1380 int unkn = 0;
mazgch 21:c4d64830bf02 1381 int sz = pipe->size();
mazgch 21:c4d64830bf02 1382 int fr = pipe->free();
mazgch 21:c4d64830bf02 1383 if (len > sz)
mazgch 21:c4d64830bf02 1384 len = sz;
mazgch 21:c4d64830bf02 1385 while (len > 0)
mazgch 21:c4d64830bf02 1386 {
mazgch 21:c4d64830bf02 1387 static struct {
mazgch 21:c4d64830bf02 1388 const char* fmt; int type;
mazgch 21:c4d64830bf02 1389 } lutF[] = {
mazgch 21:c4d64830bf02 1390 { "\r\n+USORD: %d,%d,\"%c\"", TYPE_PLUS },
mazgch 21:c4d64830bf02 1391 { "\r\n+USORF: %d,\""IPSTR"\",%d,%d,\"%c\"", TYPE_PLUS },
mazgch 80:34985b4d821e 1392 { "\r\n+URDFILE: %s,%d,\"%c\"", TYPE_PLUS },
mazgch 21:c4d64830bf02 1393 };
mazgch 21:c4d64830bf02 1394 static struct {
mazgch 21:c4d64830bf02 1395 const char* sta; const char* end; int type;
mazgch 21:c4d64830bf02 1396 } lut[] = {
mazgch 21:c4d64830bf02 1397 { "\r\nOK\r\n", NULL, TYPE_OK },
mazgch 21:c4d64830bf02 1398 { "\r\nERROR\r\n", NULL, TYPE_ERROR },
mazgch 31:a0bed6c1e05d 1399 { "\r\n+CME ERROR:", "\r\n", TYPE_ERROR },
mazgch 21:c4d64830bf02 1400 { "\r\n+CMS ERROR:", "\r\n", TYPE_ERROR },
mazgch 21:c4d64830bf02 1401 { "\r\nRING\r\n", NULL, TYPE_RING },
mazgch 21:c4d64830bf02 1402 { "\r\nCONNECT\r\n", NULL, TYPE_CONNECT },
mazgch 21:c4d64830bf02 1403 { "\r\nNO CARRIER\r\n", NULL, TYPE_NOCARRIER },
mazgch 21:c4d64830bf02 1404 { "\r\nNO DIALTONE\r\n", NULL, TYPE_NODIALTONE },
mazgch 21:c4d64830bf02 1405 { "\r\nBUSY\r\n", NULL, TYPE_BUSY },
mazgch 21:c4d64830bf02 1406 { "\r\nNO ANSWER\r\n", NULL, TYPE_NOANSWER },
mazgch 21:c4d64830bf02 1407 { "\r\n+", "\r\n", TYPE_PLUS },
mazgch 21:c4d64830bf02 1408 { "\r\n@", NULL, TYPE_PROMPT }, // Sockets
mazgch 21:c4d64830bf02 1409 { "\r\n>", NULL, TYPE_PROMPT }, // SMS
mazgch 80:34985b4d821e 1410 { "\n>", NULL, TYPE_PROMPT }, // File
mazgch 21:c4d64830bf02 1411 };
mazgch 21:c4d64830bf02 1412 for (int i = 0; i < sizeof(lutF)/sizeof(*lutF); i ++) {
mazgch 21:c4d64830bf02 1413 pipe->set(unkn);
mazgch 21:c4d64830bf02 1414 int ln = _parseFormated(pipe, len, lutF[i].fmt);
mazgch 21:c4d64830bf02 1415 if (ln == WAIT && fr)
mazgch 21:c4d64830bf02 1416 return WAIT;
mazgch 21:c4d64830bf02 1417 if ((ln != NOT_FOUND) && (unkn > 0))
mazgch 31:a0bed6c1e05d 1418 return TYPE_UNKNOWN | pipe->get(buf, unkn);
mazgch 21:c4d64830bf02 1419 if (ln > 0)
mazgch 21:c4d64830bf02 1420 return lutF[i].type | pipe->get(buf, ln);
mazgch 21:c4d64830bf02 1421 }
mazgch 21:c4d64830bf02 1422 for (int i = 0; i < sizeof(lut)/sizeof(*lut); i ++) {
mazgch 21:c4d64830bf02 1423 pipe->set(unkn);
mazgch 21:c4d64830bf02 1424 int ln = _parseMatch(pipe, len, lut[i].sta, lut[i].end);
mazgch 21:c4d64830bf02 1425 if (ln == WAIT && fr)
mazgch 21:c4d64830bf02 1426 return WAIT;
mazgch 21:c4d64830bf02 1427 if ((ln != NOT_FOUND) && (unkn > 0))
mazgch 31:a0bed6c1e05d 1428 return TYPE_UNKNOWN | pipe->get(buf, unkn);
mazgch 21:c4d64830bf02 1429 if (ln > 0)
mazgch 21:c4d64830bf02 1430 return lut[i].type | pipe->get(buf, ln);
mazgch 21:c4d64830bf02 1431 }
mazgch 21:c4d64830bf02 1432 // UNKNOWN
mazgch 21:c4d64830bf02 1433 unkn ++;
mazgch 21:c4d64830bf02 1434 len--;
mazgch 21:c4d64830bf02 1435 }
mazgch 18:e5697801df29 1436 return WAIT;
mazgch 18:e5697801df29 1437 }
mazgch 18:e5697801df29 1438
mazgch 18:e5697801df29 1439 // ----------------------------------------------------------------
mazgch 18:e5697801df29 1440 // Serial Implementation
mazgch 18:e5697801df29 1441 // ----------------------------------------------------------------
mazgch 18:e5697801df29 1442
mazgch 76:f7c3dd568dae 1443 /*! Helper Dev Null Device
mazgch 76:f7c3dd568dae 1444 Small helper class used to shut off stderr/stdout. Sometimes stdin/stdout
mazgch 76:f7c3dd568dae 1445 is shared with the serial port of the modem. Having printfs inbetween the
mazgch 76:f7c3dd568dae 1446 AT commands you cause a failure of the modem.
mazgch 76:f7c3dd568dae 1447 */
mazgch 76:f7c3dd568dae 1448 class DevNull : public Stream {
mazgch 76:f7c3dd568dae 1449 public:
mazgch 76:f7c3dd568dae 1450 DevNull() : Stream(_name+1) { } //!< Constructor
mazgch 76:f7c3dd568dae 1451 void claim(const char* mode, FILE* file)
mazgch 76:f7c3dd568dae 1452 { freopen(_name, mode, file); } //!< claim a stream
mazgch 76:f7c3dd568dae 1453 protected:
mazgch 76:f7c3dd568dae 1454 virtual int _getc() { return EOF; } //!< Nothing
mazgch 76:f7c3dd568dae 1455 virtual int _putc(int c) { return c; } //!< Discard
mazgch 76:f7c3dd568dae 1456 static const char* _name; //!< File name
mazgch 76:f7c3dd568dae 1457 };
mazgch 76:f7c3dd568dae 1458 const char* DevNull::_name = "/null"; //!< the null device name
mazgch 76:f7c3dd568dae 1459 static DevNull null; //!< the null device
mazgch 76:f7c3dd568dae 1460
mazgch 19:2b5d097ca15d 1461 MDMSerial::MDMSerial(PinName tx /*= MDMTXD*/, PinName rx /*= MDMRXD*/,
mazgch 19:2b5d097ca15d 1462 int baudrate /*= MDMBAUD*/,
mazgch 43:a89a7a505991 1463 #if DEVICE_SERIAL_FC
mazgch 19:2b5d097ca15d 1464 PinName rts /*= MDMRTS*/, PinName cts /*= MDMCTS*/,
mazgch 43:a89a7a505991 1465 #endif
mazgch 18:e5697801df29 1466 int rxSize /*= 256*/, int txSize /*= 128*/) :
mazgch 35:9275215a3a5b 1467 SerialPipe(tx, rx, rxSize, txSize)
mazgch 18:e5697801df29 1468 {
mazgch 76:f7c3dd568dae 1469 if (rx == USBRX)
mazgch 76:f7c3dd568dae 1470 null.claim("r", stdin);
mazgch 76:f7c3dd568dae 1471 if (tx == USBTX) {
mazgch 76:f7c3dd568dae 1472 null.claim("w", stdout);
mazgch 76:f7c3dd568dae 1473 null.claim("w", stderr);
mazgch 74:208e3e32d263 1474 #ifdef MDM_DEBUG
mazgch 76:f7c3dd568dae 1475 _debugLevel = -1;
mazgch 76:f7c3dd568dae 1476 #endif
mazgch 76:f7c3dd568dae 1477 }
mazgch 74:208e3e32d263 1478 #ifdef TARGET_UBLOX_C027
mazgch 74:208e3e32d263 1479 _onboard = (tx == MDMTXD) && (rx == MDMRXD);
mazgch 74:208e3e32d263 1480 if (_onboard)
mazgch 74:208e3e32d263 1481 c027_mdm_powerOn(false);
mazgch 74:208e3e32d263 1482 #endif
mazgch 18:e5697801df29 1483 baud(baudrate);
mazgch 35:9275215a3a5b 1484 #if DEVICE_SERIAL_FC
mazgch 35:9275215a3a5b 1485 if ((rts != NC) || (cts != NC))
mazgch 35:9275215a3a5b 1486 {
mazgch 35:9275215a3a5b 1487 Flow flow = (cts == NC) ? RTS :
mazgch 35:9275215a3a5b 1488 (rts == NC) ? CTS : RTSCTS ;
mazgch 35:9275215a3a5b 1489 set_flow_control(flow, rts, cts);
mazgch 35:9275215a3a5b 1490 if (cts != NC) _dev.lpm = LPM_ENABLED;
mazgch 35:9275215a3a5b 1491 }
mazgch 35:9275215a3a5b 1492 #endif
mazgch 18:e5697801df29 1493 }
mazgch 18:e5697801df29 1494
mazgch 76:f7c3dd568dae 1495 MDMSerial::~MDMSerial(void)
mazgch 76:f7c3dd568dae 1496 {
mazgch 76:f7c3dd568dae 1497 powerOff();
mazgch 76:f7c3dd568dae 1498 #ifdef TARGET_UBLOX_C027
mazgch 76:f7c3dd568dae 1499 if (_onboard)
mazgch 76:f7c3dd568dae 1500 c027_mdm_powerOff();
mazgch 76:f7c3dd568dae 1501 #endif
mazgch 76:f7c3dd568dae 1502 }
mazgch 76:f7c3dd568dae 1503
mazgch 18:e5697801df29 1504 int MDMSerial::_send(const void* buf, int len)
mazgch 18:e5697801df29 1505 {
mazgch 35:9275215a3a5b 1506 return put((const char*)buf, len, true/*=blocking*/);
mazgch 18:e5697801df29 1507 }
mazgch 18:e5697801df29 1508
mazgch 18:e5697801df29 1509 int MDMSerial::getLine(char* buffer, int length)
mazgch 18:e5697801df29 1510 {
mazgch 18:e5697801df29 1511 return _getLine(&_pipeRx, buffer, length);
mazgch 18:e5697801df29 1512 }
mazgch 18:e5697801df29 1513
mazgch 18:e5697801df29 1514 // ----------------------------------------------------------------
mazgch 18:e5697801df29 1515 // USB Implementation
mazgch 18:e5697801df29 1516 // ----------------------------------------------------------------
mazgch 18:e5697801df29 1517
mazgch 18:e5697801df29 1518 #ifdef HAVE_MDMUSB
mazgch 76:f7c3dd568dae 1519 MDMUsb::MDMUsb(void)
mazgch 74:208e3e32d263 1520 {
mazgch 74:208e3e32d263 1521 #ifdef MDM_DEBUG
mazgch 74:208e3e32d263 1522 _debugLevel = 1;
mazgch 74:208e3e32d263 1523 #endif
mazgch 74:208e3e32d263 1524 #ifdef TARGET_UBLOX_C027
mazgch 74:208e3e32d263 1525 _onboard = true;
mazgch 74:208e3e32d263 1526 c027_mdm_powerOn(true);
mazgch 74:208e3e32d263 1527 #endif
mazgch 74:208e3e32d263 1528 }
mazgch 76:f7c3dd568dae 1529
mazgch 76:f7c3dd568dae 1530 MDMUsb::~MDMUsb(void)
mazgch 76:f7c3dd568dae 1531 {
mazgch 76:f7c3dd568dae 1532 powerOff();
mazgch 76:f7c3dd568dae 1533 #ifdef TARGET_UBLOX_C027
mazgch 76:f7c3dd568dae 1534 if (_onboard)
mazgch 76:f7c3dd568dae 1535 c027_mdm_powerOff();
mazgch 76:f7c3dd568dae 1536 #endif
mazgch 76:f7c3dd568dae 1537 }
mazgch 76:f7c3dd568dae 1538
mazgch 76:f7c3dd568dae 1539 int MDMUsb::_send(const void* buf, int len) { return 0; }
mazgch 76:f7c3dd568dae 1540
mazgch 18:e5697801df29 1541 int MDMUsb::getLine(char* buffer, int length) { return NOT_FOUND; }
mazgch 76:f7c3dd568dae 1542
mazgch 35:9275215a3a5b 1543 #endif