Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
AT_CellularSMS.cpp
00001 /* 00002 * Copyright (c) 2017, Arm Limited and affiliates. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 00018 #include <time.h> 00019 #include <stdlib.h> 00020 #include "mbed_wait_api.h" 00021 #include "AT_CellularSMS.h" 00022 #include "CellularUtil.h" 00023 #include "CellularLog.h" 00024 00025 using namespace mbed_cellular_util; 00026 using namespace mbed; 00027 using namespace std; 00028 00029 #define CTRL_Z "\x1a" 00030 #define ESC "\x1b" 00031 00032 const uint8_t SMS_STATUS_SIZE = 12 + 1; 00033 const uint8_t FIRST_OCTET_DELIVER_SUBMIT = 17; 00034 const uint8_t TP_VALIDITY_PERIOD_24_HOURS = 167; 00035 const uint8_t TP_PROTOCOL_IDENTIFIER = 0; 00036 const uint8_t SMS_DATA_CODING_SCHEME = 0; 00037 00038 const uint8_t SMS_MAX_8BIT_CONCATENATED_SINGLE_SMS_SIZE = 134; 00039 const uint8_t SMS_MAX_GSM7_CONCATENATED_SINGLE_SMS_SIZE = 153; 00040 #define NVAM '?' // Not Valid ascii, ISO-8859-1 mark 00041 00042 // mapping table from 7-bit GSM to ascii (ISO-8859-1) 00043 static const int gsm_to_ascii[] = { 00044 64, // 0 00045 163, // 1 00046 36, // 2 00047 165, // 3 00048 232, // 4 00049 233, // 5 00050 249, // 6 00051 236, // 7 00052 242, // 8 00053 199, // 9 00054 10, // 10 00055 216, // 11 00056 248, // 12 00057 13, // 13 00058 197, // 14 00059 229, // 15 00060 NVAM, // 16 00061 95, // 17 00062 NVAM, // 18 00063 NVAM, // 19 00064 NVAM, // 20 00065 NVAM, // 21 00066 NVAM, // 22 00067 NVAM, // 23 00068 NVAM, // 24 00069 NVAM, // 25 00070 NVAM, // 26 00071 27, // 27 00072 198, // 28 00073 230, // 29 00074 223, // 30 00075 201, // 31 00076 32, // 32 00077 33, // 33 00078 34, // 34 00079 35, // 35 00080 164, // 36 00081 37, // 37 00082 38, // 38 00083 39, // 39 00084 40, // 40 00085 41, // 41 00086 42, // 42 00087 43, // 43 00088 44, // 44 00089 45, // 45 00090 46, // 46 00091 47, // 47 00092 48, // 48 00093 49, // 49 00094 50, // 50 00095 51, // 51 00096 52, // 52 00097 53, // 53 00098 54, // 54 00099 55, // 55 00100 56, // 56 00101 57, // 57 00102 58, // 58 00103 59, // 59 00104 60, // 60 00105 61, // 61 00106 62, // 62 00107 63, // 63 00108 161, // 64 00109 65, // 65 00110 66, // 66 00111 67, // 67 00112 68, // 68 00113 69, // 69 00114 70, // 70 00115 71, // 71 00116 72, // 72 00117 73, // 73 00118 74, // 74 00119 75, // 75 00120 76, // 76 00121 77, // 77 00122 78, // 78 00123 79, // 79 00124 80, // 80 00125 81, // 81 00126 82, // 82 00127 83, // 83 00128 84, // 84 00129 85, // 85 00130 86, // 86 00131 87, // 87 00132 88, // 88 00133 89, // 89 00134 90, // 90 00135 196, // 91 00136 214, // 92 00137 209, // 93 00138 220, // 94 00139 167, // 95 00140 191, // 96 00141 97, // 97 00142 98, // 98 00143 99, // 99 00144 100, // 100 00145 101, // 101 00146 102, // 102 00147 103, // 103 00148 104, // 104 00149 105, // 105 00150 106, // 106 00151 107, // 107 00152 108, // 108 00153 109, // 109 00154 110, // 110 00155 111, // 111 00156 112, // 112 00157 113, // 113 00158 114, // 114 00159 115, // 115 00160 116, // 116 00161 117, // 117 00162 118, // 118 00163 119, // 119 00164 120, // 120 00165 121, // 121 00166 122, // 122 00167 228, // 123 00168 246, // 124 00169 241, // 125 00170 252, // 126 00171 224 // 127 00172 }; 00173 00174 const int GSM_TO_ASCII_TABLE_SIZE = sizeof(gsm_to_ascii)/sizeof(gsm_to_ascii[0]); 00175 00176 AT_CellularSMS::AT_CellularSMS(ATHandler &at) : AT_CellularBase(at), _cb(0), _mode(CellularSMSMmodeText), 00177 _use_8bit_encoding(false), _sim_wait_time(0), _sms_message_ref_number(1), _sms_info(NULL) 00178 { 00179 } 00180 00181 AT_CellularSMS::~AT_CellularSMS() 00182 { 00183 } 00184 00185 void AT_CellularSMS::cmt_urc() 00186 { 00187 tr_debug("CMT_URC called"); 00188 //+CMT: <oa>,[<alpha>],<scts>[,<tooa>,<fo>,<pid>,<dcs>,<sca>,<tosca>,<length>]<CR><LF><data> 00189 _at.consume_to_stop_tag(); 00190 // call user defined callback function 00191 if (_cb) { 00192 _cb(); 00193 } else { 00194 tr_warn("cmt_urc, no user defined callback for receiving sms!"); 00195 } 00196 } 00197 00198 void AT_CellularSMS::cmti_urc() 00199 { 00200 //+CMTI: <mem>,<index>, 00201 tr_debug("CMTI_URC called"); 00202 // call user defined callback function 00203 if (_cb) { 00204 _cb(); 00205 } else { 00206 tr_warn("cmti_urc, no user defined callback for receiving sms!"); 00207 } 00208 } 00209 00210 nsapi_error_t AT_CellularSMS::set_cnmi() 00211 { 00212 _at.lock(); 00213 _at.cmd_start("AT+CNMI=2,1"); 00214 _at.cmd_stop(); 00215 _at.resp_start(); 00216 _at.resp_stop(); 00217 return _at.unlock_return_error(); 00218 } 00219 00220 nsapi_error_t AT_CellularSMS::set_cmgf(int msg_format) 00221 { 00222 _at.lock(); 00223 _at.cmd_start("AT+CMGF="); 00224 _at.write_int(msg_format); 00225 _at.cmd_stop(); 00226 _at.resp_start(); 00227 _at.resp_stop(); 00228 return _at.unlock_return_error(); 00229 } 00230 00231 nsapi_error_t AT_CellularSMS::set_csmp(int fo, int vp, int pid, int dcs) 00232 { 00233 _at.lock(); 00234 _at.cmd_start("AT+CSMP="); 00235 _at.write_int(fo); 00236 _at.write_int(vp); 00237 _at.write_int(pid); 00238 _at.write_int(dcs); 00239 _at.cmd_stop(); 00240 _at.resp_start(); 00241 _at.resp_stop(); 00242 return _at.unlock_return_error(); 00243 } 00244 00245 nsapi_error_t AT_CellularSMS::set_csdh(int show_header) 00246 { 00247 _at.lock(); 00248 _at.cmd_start("AT+CSDH="); 00249 _at.write_int(show_header); 00250 _at.cmd_stop(); 00251 _at.resp_start(); 00252 _at.resp_stop(); 00253 return _at.unlock_return_error(); 00254 } 00255 00256 nsapi_error_t AT_CellularSMS::initialize(CellularSMSMmode mode) 00257 { 00258 if (_at.set_urc_handler("+CMTI:", callback(this, &AT_CellularSMS::cmti_urc)) || 00259 _at.set_urc_handler("+CMT:", callback(this, &AT_CellularSMS::cmt_urc))) { 00260 return NSAPI_ERROR_NO_MEMORY ; 00261 } 00262 00263 _at.lock(); 00264 set_cnmi(); //set new SMS indication 00265 set_cmgf(mode); //set message format/PDU 00266 00267 if (mode == CellularSMSMmodeText) { 00268 set_csmp(FIRST_OCTET_DELIVER_SUBMIT, TP_VALIDITY_PERIOD_24_HOURS, TP_PROTOCOL_IDENTIFIER, 00269 SMS_DATA_CODING_SCHEME); //set Set Text Mode Parameters(default values for SMS-SUBMIT and RECEIVE) 00270 set_csdh(1);//set header extra info as it's needed 00271 } 00272 00273 _mode = mode; 00274 00275 return _at.unlock_return_error(); 00276 } 00277 00278 void AT_CellularSMS::set_extra_sim_wait_time(int sim_wait_time) 00279 { 00280 _sim_wait_time = sim_wait_time; 00281 } 00282 00283 char* AT_CellularSMS::create_pdu(const char* phone_number, const char* message, uint8_t message_length, uint8_t msg_parts, 00284 uint8_t msg_part_number, uint8_t& header_size) 00285 { 00286 int totalPDULength = 0; 00287 int number_len = strlen(phone_number); 00288 00289 totalPDULength += number_len; 00290 if (number_len&0x01) {// if phone number length is not even length we must pad it and so +1 00291 totalPDULength += 1; 00292 } 00293 00294 totalPDULength += 16; // all other than phone number and message length 00295 if (msg_parts > 1) {// add more space for UDH 00296 totalPDULength += 12; 00297 } 00298 // there might be need for padding so some more space 00299 totalPDULength +=2; 00300 00301 // message 7-bit padded and it will be converted to hex so it will take twice as much space 00302 totalPDULength += (message_length - (message_length/8))*2; 00303 00304 char* pdu = (char*)calloc(totalPDULength, sizeof(char)); 00305 if (!pdu) { 00306 return NULL; 00307 } 00308 00309 int x = 0; 00310 // See more how to create PDU from 3GPP specification 23040 00311 // first two define that we use service center number which is set with +CSCA 00312 pdu[x++] = '0'; 00313 pdu[x++] = '0'; 00314 // First Octet of the TPDU. 41 means SMS SUBMIT, no validity period, no status report, use User Data Header. 00315 // 01 means SMS SUBMIT, no validity period, no status report, NO User Data Header. 00316 if (msg_parts > 1) { // concatenated, must use UDH 00317 pdu[x++] = '4'; 00318 } else { 00319 pdu[x++] = '0'; 00320 } 00321 pdu[x++] = '1'; 00322 // assign a message reference automatically. We have defined TP-RD bit as 0 so duplicates are not rejected. 00323 pdu[x++] = '0'; 00324 pdu[x++] = '0'; 00325 // [6] and [7] Length of the Destination Phone Number 00326 int_to_hex_str(number_len, pdu+x); 00327 x+=2; 00328 // Type of the Destination Phone Number 00329 pdu[x++] = '8'; 00330 pdu[x++] = '1'; 00331 00332 // phone number as reverse nibble encoded 00333 int i = 0; 00334 for (; i < number_len; i += 2) { 00335 if (i+1 == number_len) { 00336 pdu[x++] = 'f'; 00337 } else { 00338 pdu[x++] = phone_number[i+1]; 00339 } 00340 pdu[x++] = phone_number[i]; 00341 } 00342 00343 // Protocol Identifier 00344 pdu[x++] = '0'; 00345 pdu[x++] = '0'; 00346 // Data Coding Scheme, GSM 7-bit default alphabet = '00', 8-bit '04' 00347 pdu[x++] = '0'; 00348 00349 if (_use_8bit_encoding) { 00350 pdu[x++] = '4'; 00351 } else { 00352 pdu[x++] = '0'; 00353 } 00354 00355 uint8_t udhlen = 0; 00356 // Length can be update after we have created PDU, store position for later use. 00357 int lengthPos = x; 00358 x +=2; 00359 00360 int paddingBits = 0; 00361 if (msg_parts > 1) { // concatenated, must use UDH 00362 // user data header length in chars 00363 pdu[x++] = '0'; 00364 udhlen = 6; // udh length in chars (5) + udhl length in chars 00365 pdu[x++] = '5'; 00366 // Information element identifier 00367 pdu[x++] = '0'; 00368 pdu[x++] = '0'; 00369 // Information element data length 00370 pdu[x++] = '0'; 00371 pdu[x++] = '3'; 00372 // A reference number (must be the same for all parts of the same larger messages) 00373 int_to_hex_str(_sms_message_ref_number&0xFF, pdu+x); 00374 x +=2; 00375 // How many parts does this message have? 00376 int_to_hex_str(msg_parts, pdu+x); 00377 x +=2; 00378 // this is a part number 00379 int_to_hex_str(msg_part_number, pdu+x); 00380 x +=2; 00381 00382 // if there is padding bits then udhlen is octet bigger as we need to keep septet boundary 00383 paddingBits = (udhlen * 8 ) % 7; 00384 if (paddingBits) { 00385 paddingBits = 7 - paddingBits; 00386 udhlen += 1; 00387 } 00388 } 00389 00390 if (_use_8bit_encoding) { 00391 char_str_to_hex_str(message, message_length, pdu+x); 00392 } else { 00393 // we might need to send zero length sms 00394 if (message_length) { 00395 if (pack_7_bit_gsm_and_hex(message, message_length, pdu+x, paddingBits) == 0) { 00396 free(pdu); 00397 return NULL; 00398 } 00399 } 00400 } 00401 00402 // now we know the correct length of the UDL (User Data Length) 00403 int_to_hex_str(message_length + udhlen, pdu+lengthPos); 00404 header_size = x; 00405 00406 return pdu; 00407 } 00408 00409 nsapi_size_or_error_t AT_CellularSMS::send_sms(const char* phone_number, const char* message, int msg_len) 00410 { 00411 int single_sms_max_length = _use_8bit_encoding ? SMS_MAX_SIZE_8BIT_SINGLE_SMS_SIZE : 00412 SMS_MAX_SIZE_GSM7_SINGLE_SMS_SIZE; 00413 if ((_mode == CellularSMSMmodeText && msg_len > single_sms_max_length) || !phone_number) { 00414 return NSAPI_ERROR_PARAMETER ; 00415 } 00416 00417 _at.lock(); 00418 00419 int write_size = 0; 00420 int remove_plus_sign = (phone_number[0] == '+') ? 1 : 0; 00421 00422 wait_ms(_sim_wait_time); 00423 00424 if (_mode == CellularSMSMmodeText) { 00425 _at.cmd_start("AT+CMGS="); 00426 _at.write_string(phone_number+remove_plus_sign); 00427 _at.cmd_stop(); 00428 00429 wait_ms(_sim_wait_time); 00430 _at.resp_start("> ", true); 00431 00432 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00433 write_size = _at.write_bytes((uint8_t*)message, msg_len); 00434 if (write_size < msg_len) { 00435 // sending can be cancelled by giving <ESC> character (IRA 27). 00436 _at.cmd_start(ESC); 00437 _at.cmd_stop(); 00438 _at.unlock(); 00439 return write_size; 00440 } 00441 // <ctrl-Z> (IRA 26) must be used to indicate the ending of the message body. 00442 _at.cmd_start(CTRL_Z); 00443 _at.cmd_stop(); 00444 _at.resp_start("+CMGS:"); 00445 _at.resp_stop(); 00446 } 00447 } else { 00448 // supports uncompressed 8 bit data and GSM 7 bit default alphabet data. Current implementation uses only 00449 // GSM 7 bit default but support is done for 8 bit data. 00450 int sms_count; 00451 int concatenated_sms_length = _use_8bit_encoding ? SMS_MAX_8BIT_CONCATENATED_SINGLE_SMS_SIZE : 00452 SMS_MAX_GSM7_CONCATENATED_SINGLE_SMS_SIZE; 00453 00454 if (msg_len <= single_sms_max_length) { 00455 // single message 00456 sms_count = 1; 00457 } else { 00458 // concatenated message 00459 sms_count = msg_len/concatenated_sms_length; 00460 if (msg_len%concatenated_sms_length != 0) { 00461 sms_count++; 00462 } 00463 } 00464 00465 int remaining_len = msg_len; 00466 int pdu_len; 00467 int msg_write_len = 0; 00468 uint8_t header_len; 00469 char *pdu_str; 00470 for (int i = 0; i < sms_count; i++) { 00471 00472 header_len = 0; 00473 if (sms_count == 1) { 00474 pdu_len = msg_len; 00475 } else { 00476 pdu_len = remaining_len > concatenated_sms_length ? concatenated_sms_length : remaining_len; 00477 } 00478 00479 pdu_str = create_pdu(phone_number+remove_plus_sign, message + i*concatenated_sms_length, pdu_len, 00480 sms_count, i+1, header_len); 00481 if (!pdu_str) { 00482 _at.unlock(); 00483 return NSAPI_ERROR_NO_MEMORY ; 00484 } 00485 pdu_len = strlen(pdu_str); 00486 00487 // specification says that service center number should not be included so we subtract -2 from pdu_len as we use '00' for automatic service center number 00488 _at.cmd_start("AT+CMGS="); 00489 _at.write_int((pdu_len-2)/2); 00490 _at.cmd_stop(); 00491 00492 wait_ms(_sim_wait_time); 00493 _at.resp_start("> ", true); 00494 00495 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00496 write_size = _at.write_bytes((uint8_t*)pdu_str, pdu_len); 00497 if (write_size < pdu_len) { 00498 // calculate exact size of what we have send 00499 if (write_size <= header_len) { 00500 // managed only to write header or some of it so actual msg write size in this iteration is 0 00501 write_size = 0; 00502 } else { 00503 write_size = (write_size - header_len)/2; // as hex encoded so divide by two 00504 } 00505 msg_write_len += write_size; 00506 00507 // sending can be cancelled by giving <ESC> character (IRA 27). 00508 _at.cmd_start(ESC); 00509 _at.cmd_stop(); 00510 _at.unlock(); 00511 free(pdu_str); 00512 return msg_write_len; 00513 } 00514 00515 // <ctrl-Z> (IRA 26) must be used to indicate the ending of the message body. 00516 _at.cmd_start(CTRL_Z); 00517 _at.cmd_stop(); 00518 _at.resp_start("+CMGS:"); 00519 _at.resp_stop(); 00520 } 00521 free(pdu_str); 00522 remaining_len -= concatenated_sms_length; 00523 if (_at.get_last_error() != NSAPI_ERROR_OK ) { 00524 return _at.unlock_return_error(); 00525 } 00526 } 00527 } 00528 00529 _sms_message_ref_number++; 00530 nsapi_error_t ret = _at.get_last_error(); 00531 _at.unlock(); 00532 00533 return (ret == NSAPI_ERROR_OK ) ? msg_len : ret; 00534 } 00535 00536 void AT_CellularSMS::set_sms_callback(Callback<void()> func) 00537 { 00538 _cb = func; 00539 } 00540 00541 nsapi_error_t AT_CellularSMS::set_cpms(const char *memr, const char *memw, const char *mems) 00542 { 00543 _at.lock(); 00544 _at.cmd_start("AT+CPMS="); 00545 _at.write_string(memr); 00546 _at.write_string(memw); 00547 _at.write_string(mems); 00548 _at.cmd_stop(); 00549 _at.resp_start(); 00550 _at.resp_stop(); 00551 00552 return _at.unlock_return_error(); 00553 } 00554 00555 nsapi_error_t AT_CellularSMS::set_csca(const char *sca, int type) 00556 { 00557 _at.lock(); 00558 _at.cmd_start("AT+CSCA="); 00559 _at.write_string(sca); 00560 _at.write_int(type); 00561 _at.cmd_stop(); 00562 _at.resp_start(); 00563 _at.resp_stop(); 00564 00565 return _at.unlock_return_error(); 00566 } 00567 00568 nsapi_size_or_error_t AT_CellularSMS::set_cscs(const char *chr_set) 00569 { 00570 _at.lock(); 00571 _at.cmd_start("AT+CSCS="); 00572 _at.write_string(chr_set); 00573 _at.cmd_stop(); 00574 _at.resp_start(); 00575 _at.resp_stop(); 00576 00577 return _at.unlock_return_error(); 00578 } 00579 00580 nsapi_error_t AT_CellularSMS::delete_sms(sms_info_t* sms) 00581 { 00582 _at.lock(); 00583 for (int i = 0; i < sms->parts; i++) { 00584 _at.cmd_start("AT+CMGD="); 00585 _at.write_int(sms->msg_index[i]); 00586 _at.cmd_stop(); 00587 _at.resp_start(); 00588 _at.resp_stop(); 00589 } 00590 00591 return _at.unlock_return_error(); 00592 } 00593 00594 // we need this as for example concatenated sms can get different sms reference numbers 00595 // if for example last part take much more time to arrive. This situation happened while testing. 00596 // What this means that after this we can't read another sms because we always read the oldest sms 00597 // that was corrupted. So we need to have delete all messages. 00598 nsapi_error_t AT_CellularSMS::delete_all_messages() 00599 { 00600 _at.lock(); 00601 _at.cmd_start("AT+CMGD=1,4"); 00602 _at.cmd_stop(); 00603 _at.resp_start(); 00604 _at.resp_stop(); 00605 return _at.unlock_return_error(); 00606 } 00607 00608 // read msg in text mode 00609 nsapi_size_or_error_t AT_CellularSMS::read_sms_from_index(int msg_index, char* buf, uint16_t len, char* phone_num, 00610 char* time_stamp) 00611 { 00612 /* 00613 * +CMGR: <stat>,<oa>,<alpha>,<scts>[,<tooa>,<fo>,<pid>,<dcs>,<sca>,<tosca>,<length>]<CR><LF><data><CR><LF>OK<CR><LF> 00614 */ 00615 wait_ms(_sim_wait_time); 00616 _at.cmd_start("AT+CMGR="); 00617 _at.write_int(msg_index); 00618 _at.cmd_stop(); 00619 00620 // TODO: NOTE: If the selected <mem1> can contain different types of SMs (e.g. SMS-DELIVERs, SMS-SUBMITs, SMS-STATUS-REPORTs and SMS-COMMANDs), 00621 // the response may be a mix of the responses of different SM types. TE application can recognize the response format by examining the third response parameter. 00622 // for now we support sms reading of received messages 00623 int buf_len = 0; 00624 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00625 char status[SMS_STATUS_SIZE]; 00626 // first we read msg status and with that we can decide how the rest of message format 00627 _at.resp_start("+CMGR:"); 00628 00629 if (_at.info_resp()) { 00630 _at.read_string(status, SMS_STATUS_SIZE); 00631 uint16_t status_len = strlen(status); 00632 if (((status_len == sizeof("REC READ") - 1) && memcmp("REC READ", status, status_len) == 0) 00633 || ((status_len == sizeof("REC UNREAD") - 1) && memcmp("REC UNREAD", status, status_len) == 0)) { 00634 // Received message 00635 if (phone_num) { 00636 _at.read_string(phone_num, SMS_MAX_PHONE_NUMBER_SIZE); 00637 } 00638 else { 00639 _at.skip_param(); // <oa>,<alpha> 00640 } 00641 _at.skip_param(); // <alpha> 00642 if (time_stamp) { 00643 _at.read_string(time_stamp, SMS_MAX_TIME_STAMP_SIZE); 00644 } 00645 (void)_at.consume_to_stop_tag(); // consume until <CR><LF> 00646 if (buf) { 00647 _at.read_string(buf, len); 00648 buf_len = strlen(buf); 00649 } 00650 } 00651 } 00652 _at.resp_stop(); 00653 } 00654 00655 return (_at.get_last_error() == NSAPI_ERROR_OK ) ? buf_len : _at.get_last_error(); 00656 } 00657 00658 // read msg in PDU mode 00659 nsapi_size_or_error_t AT_CellularSMS::read_sms(sms_info_t* sms, char* buf, char* phone_num, char* time_stamp) 00660 { 00661 // +CMGR: <stat>,[<alpha>],<length><CR><LF><pdu> 00662 int index; 00663 if (sms->parts == sms->parts_added) { 00664 char *pdu; // we need a temp buffer as payload is hexencoded ---> can't use buf as it might be enough for message but not hexenconded pdu. 00665 int status; 00666 int msg_len; 00667 index = 0; 00668 int pduSize; 00669 00670 for (int i = 0; i < sms->parts; i++) { 00671 wait_ms(_sim_wait_time); 00672 _at.cmd_start("AT+CMGR="); 00673 _at.write_int(sms->msg_index[i]); 00674 _at.cmd_stop(); 00675 _at.resp_start("+CMGR:"); 00676 00677 if (_at.info_resp()) { 00678 status = _at.read_int(); 00679 _at.skip_param(); // <alpha> 00680 if ((_at.get_last_error() == NSAPI_ERROR_OK ) && (status == 0 || status == 1)) { 00681 msg_len = _at.read_int(); 00682 if (msg_len > 0) { 00683 pduSize = msg_len*2 + 20;// *2 as it's hex encoded and +20 as service center number is not included in size given by CMGR 00684 pdu = (char*)calloc(pduSize, sizeof(char)); 00685 if (!pdu) { 00686 _at.resp_stop(); 00687 return NSAPI_ERROR_NO_MEMORY ; 00688 } 00689 _at.read_string(pdu, pduSize, true); 00690 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00691 msg_len = get_data_from_pdu(pdu, NULL, NULL, phone_num, buf+index); 00692 if (msg_len >= 0) { // we need to allow zero length messages 00693 index += msg_len; 00694 } else { 00695 free(pdu); 00696 _at.resp_stop(); 00697 return -1; 00698 } 00699 } 00700 free(pdu); 00701 } 00702 } 00703 } 00704 _at.resp_stop(); 00705 } 00706 00707 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00708 if (time_stamp) { 00709 strcpy(time_stamp, sms->date); 00710 } 00711 buf[index] = '\0'; 00712 } 00713 } 00714 else { 00715 tr_warn("NOT all concatenated parts were received..."); 00716 index = SMS_ERROR_MULTIPART_ALL_PARTS_NOT_READ; 00717 } 00718 00719 return index; 00720 } 00721 00722 nsapi_size_or_error_t AT_CellularSMS::get_sms(char* buf, uint16_t len, char* phone_num, uint16_t phone_len, 00723 char* time_stamp, uint16_t time_len, int *buf_size) 00724 { 00725 // validate buffer sizes already here to avoid any necessary function calls and locking of _at 00726 if ((phone_num && phone_len < SMS_MAX_PHONE_NUMBER_SIZE) || (time_stamp && time_len < SMS_MAX_TIME_STAMP_SIZE) || 00727 buf == NULL) { 00728 return NSAPI_ERROR_PARAMETER ; 00729 } 00730 00731 _at.lock(); 00732 00733 nsapi_size_or_error_t err = list_messages(); 00734 if (err == NSAPI_ERROR_OK ) { 00735 // we return the oldest sms and delete it after successful read 00736 sms_info_t* info = get_oldest_sms_index(); 00737 00738 if (info) { 00739 if (info->msg_size+1 > len) { // +1 for '\0' 00740 tr_warn("Given buf too small, len is: %d but is must be: %d", len, info->msg_size); 00741 if (buf_size) { 00742 *buf_size = info->msg_size; 00743 } 00744 free_linked_list(); 00745 _at.unlock(); 00746 return NSAPI_ERROR_PARAMETER ; 00747 } 00748 00749 if (_mode == CellularSMSMmodePDU) { 00750 err = read_sms(info, buf, phone_num, time_stamp); 00751 } else { 00752 err = read_sms_from_index(info->msg_index[0], buf, len, phone_num, time_stamp); 00753 } 00754 00755 if (err > 0) { 00756 int delerr = delete_sms(info); 00757 if (delerr) { 00758 err = delerr; 00759 } 00760 } 00761 } else { 00762 // No messages were found, return -1 00763 err = -1; 00764 } 00765 } 00766 00767 free_linked_list(); 00768 00769 _at.unlock(); 00770 00771 // update error only when there really was an error, otherwise we return the length 00772 if (_at.get_last_error()) { 00773 err = _at.get_last_error(); 00774 } 00775 return err; 00776 } 00777 00778 nsapi_size_or_error_t AT_CellularSMS::get_data_from_pdu(const char* pdu, sms_info_t *info, int *part_number, 00779 char *phone_number, char *msg) 00780 { 00781 int index = 0; 00782 int tmp; 00783 bool userDataHeader; 00784 int oaLength; 00785 int dataScheme; 00786 nsapi_size_or_error_t err = NSAPI_ERROR_OK ; 00787 00788 // read Length of the SMSC information 00789 oaLength = hex_str_to_int(pdu, 2); 00790 index += 2; // length we just read 00791 index += oaLength*2; // skip service center number 00792 00793 // read first the lower part of first octet as there is message type 00794 index++; 00795 tmp = hex_str_to_int(pdu+index, 1); 00796 //wait_ms(200); 00797 if ((tmp & 0x03) == 0) {// SMS-DELIVER type, last two bits should be zero 00798 // UDH present? Check from first octets higher part 00799 tmp = hex_str_to_int(pdu + (--index), 1); 00800 userDataHeader = ((tmp & 0x04) == 0) ? false : true; 00801 00802 index +=2; // we just read the high bits of first octet so move +2 00803 // originating address length 00804 oaLength = hex_str_to_int(pdu+index, 2); 00805 index +=2; // add index over address length 00806 index +=2; // skip number type 00807 if (phone_number) { 00808 // phone number as reverse nibble encoded 00809 int a = 0; 00810 for (; a < oaLength; a +=2) { 00811 if (a+1 == oaLength) { 00812 phone_number[a] = pdu[index+a+1]; 00813 } else { 00814 phone_number[a] = pdu[index+a+1]; 00815 phone_number[a+1] = pdu[index+a]; 00816 } 00817 } 00818 phone_number[oaLength] = '\0'; 00819 } 00820 00821 index += oaLength; 00822 if (oaLength&0x01) { // if phone number length is odd then it has padded F so skip that 00823 index++; 00824 } 00825 index +=2; // skip TP-Protocol identifier 00826 00827 dataScheme = hex_str_to_int(pdu+index, 2); 00828 index +=2; // skip TP-Data-Coding-Scheme 00829 00830 // next one is date, it's length is 7 octets according to 3GPP TS 23.040 00831 // create time string 00832 if (info) { 00833 int i = 0; 00834 // year 00835 info->date[i++] = pdu[index+1]; 00836 info->date[i++] = pdu[index]; 00837 index+=2; 00838 info->date[i++] = '/'; 00839 // month 00840 info->date[i++] = pdu[index+1]; 00841 info->date[i++] = pdu[index]; 00842 index+=2; 00843 info->date[i++] = '/'; 00844 // Day 00845 info->date[i++] = pdu[index+1]; 00846 info->date[i++] = pdu[index]; 00847 index+=2; 00848 info->date[i++] = ','; 00849 // Hour 00850 info->date[i++] = pdu[index+1]; 00851 info->date[i++] = pdu[index]; 00852 index+=2; 00853 info->date[i++] = ':'; 00854 // Minute 00855 info->date[i++] = pdu[index+1]; 00856 info->date[i++] = pdu[index]; 00857 index+=2; 00858 info->date[i++] = ':'; 00859 // Second 00860 info->date[i++] = pdu[index+1]; 00861 info->date[i++] = pdu[index]; 00862 index+=2; 00863 // timezone related to GMT. pdu[index+1] most significant bit indicates the sign related to gmt 00864 tmp = hex_str_to_int(pdu+index+1, 1); 00865 if (tmp&0x08) { 00866 info->date[i++] = '-'; 00867 } else { 00868 info->date[i++] = '+'; 00869 } 00870 00871 // pdu[index+1 & 0x07 is the most significant bits of the timezone 00872 // pdu [index] is the least significant bits 00873 info->date[i++] = '0' + (tmp & 0x07); 00874 info->date[i++] = pdu[index]; 00875 info->date[i] = '\0'; 00876 index+=2; 00877 } else { 00878 index+=14; 00879 } 00880 00881 int udl = hex_str_to_int(pdu+index, 2); 00882 index +=2; 00883 00884 int paddingBits = 0; 00885 int partnro = 1; 00886 if (userDataHeader) { 00887 // we need to read User Defined Header to know what part number this message is. 00888 index += read_udh_from_pdu(pdu+index, info, partnro, paddingBits); 00889 } 00890 00891 if (part_number) { 00892 *part_number = partnro; 00893 } 00894 00895 if (msg) { 00896 // we are reading the message 00897 err = read_pdu_payload(pdu+index, udl, dataScheme, msg, paddingBits); 00898 } 00899 else { 00900 if (dataScheme == 0x00) { 00901 // when listing messages we need to calculated length. Other way would be unpacking the whole message. 00902 err = strlen(pdu+index) >> 1; 00903 err *= 8; 00904 err /= 7; 00905 } else if (dataScheme == 0x04) { 00906 err = strlen(pdu+index) >> 1; 00907 } else { 00908 return NSAPI_ERROR_UNSUPPORTED ; 00909 } 00910 } 00911 00912 return err; 00913 } 00914 else { 00915 // message was not DELIVER so discard it 00916 return NSAPI_ERROR_UNSUPPORTED ; 00917 } 00918 } 00919 00920 // read params from User Defined Header 00921 int AT_CellularSMS::read_udh_from_pdu(const char* pdu, sms_info_t *info, int &part_number, int &padding_bits) { 00922 00923 int index = 0; 00924 int udhLength = hex_str_to_int(pdu, 2); 00925 index +=2; 00926 00927 // if there is padding bits then udhlen is octet bigger as we need to keep septet boundary 00928 padding_bits = ((udhLength+1) * 8 ) % 7; // +1 is for udhLength itself 00929 00930 if (padding_bits) { 00931 padding_bits = 7 - padding_bits; 00932 } else { 00933 padding_bits = 0; 00934 } 00935 00936 int tmp = hex_str_to_int(pdu+index, 2); 00937 index +=4; 00938 00939 if (tmp == 0) { // 8-bit reference number 00940 if (info) { 00941 info->msg_ref_number = (uint16_t)hex_str_to_int(pdu+index, 2); 00942 } 00943 index +=2; 00944 } else { // 16-bit reference number 00945 if (info) { 00946 info->msg_ref_number = (uint16_t)hex_str_to_int(pdu+index+2, 2); 00947 tmp = hex_str_to_int(pdu+index, 2); 00948 info->msg_ref_number |= (tmp << 8); 00949 } 00950 index +=4; 00951 } 00952 00953 if (info) { 00954 info->parts = hex_str_to_int(pdu+index, 2); 00955 } 00956 index +=2; 00957 00958 part_number = hex_str_to_int(pdu+index, 2); 00959 index +=2; 00960 00961 return (udhLength*2 + 2); // udh in hex and udhl 00962 } 00963 00964 nsapi_size_or_error_t AT_CellularSMS::read_pdu_payload(const char* pdu, int msg_len, int scheme, char *msg, int padding_bits) 00965 { 00966 if (scheme == 0x00) { 00967 // 7 bit gsm encoding, must do the conversions from hex to 7-bit encoding and to ascii 00968 return unpack_7_bit_gsm_to_str(pdu, strlen(pdu)/2, msg, padding_bits, msg_len); 00969 } else if (scheme == 0x04) { 00970 // 8bit scheme so just convert hexstring to charstring 00971 return hex_str_to_char_str(pdu, strlen(pdu), msg); 00972 } else { 00973 tr_error("Received unsupported data coding scheme: 0x%02x", scheme); 00974 return NSAPI_ERROR_UNSUPPORTED ; 00975 } 00976 } 00977 00978 void AT_CellularSMS::free_linked_list() 00979 { 00980 sms_info_t* info = _sms_info; 00981 sms_info_t* old; 00982 while (info) { 00983 old = info; 00984 info = info->next_info; 00985 delete old; 00986 } 00987 _sms_info = NULL; 00988 } 00989 00990 void AT_CellularSMS::add_info(sms_info_t* info, int index, int part_number) { 00991 // check for same message reference id. If found, update it and delete the given info. 00992 // if NOT found then add to the end of the list. 00993 00994 if (!_sms_info) { 00995 info->msg_index[part_number-1] = index; // part numbering starts from 1 so -1 to put to right index 00996 _sms_info = info; 00997 return; 00998 } 00999 sms_info_t* current = _sms_info; 01000 sms_info_t* prev; 01001 bool found_msg = false; 01002 while (current) { 01003 prev = current; 01004 // sms messages can have same reference number so additional checks are needed. 01005 // TODO: should we include phone number also? 01006 if (current->msg_ref_number == info->msg_ref_number && current->parts > current->parts_added && 01007 info->parts > info->parts_added) { 01008 // multipart sms, update msg size and index 01009 current->msg_size += info->msg_size; 01010 current->msg_index[part_number-1] = index; // part numbering starts from 1 so -1 to put to right index 01011 current->parts_added++; 01012 // update oldest part as date 01013 if (compare_time_strings(info->date, current->date) == -1) { 01014 strcpy(current->date, info->date); 01015 } 01016 found_msg = true; 01017 break; 01018 } 01019 current = current->next_info; 01020 } 01021 01022 if (found_msg) { 01023 // info was added to existing item in linked list, must be deleted 01024 delete info; 01025 } else { 01026 // message not found, add to linked list 01027 info->msg_index[part_number-1] = index; 01028 prev->next_info = info; 01029 } 01030 } 01031 01032 // reads all the messages to the linked list AT_CellularSMS::_sms_info 01033 nsapi_error_t AT_CellularSMS::list_messages() 01034 { 01035 // TODO: NOTE: If the selected <mem1> can contain different types of SMs (e.g. SMS-DELIVERs, SMS-SUBMITs, SMS-STATUS-REPORTs and SMS-COMMANDs), 01036 // the response may be a mix of the responses of different SM types. TE application can recognize the response format by examining the third response parameter. 01037 // for now we assume that only SMS-DELIVER messages are read. 01038 if (_mode == CellularSMSMmodePDU) { 01039 _at.cmd_start("AT+CMGL=4"); 01040 } else { 01041 _at.cmd_start("AT+CMGL=\"ALL\""); 01042 } 01043 _at.cmd_stop(); 01044 01045 sms_info_t* info = NULL; 01046 // init for 1 so that in text mode we will add to the correct place without any additional logic in addInfo() in text mode 01047 int part_number = 1; 01048 int index = 0; 01049 int length = 0; 01050 char *pdu = NULL; 01051 01052 _at.resp_start("+CMGL:"); 01053 while (_at.info_resp()) { 01054 info = new sms_info_t(); 01055 if (!info) { 01056 _at.resp_stop(); 01057 return NSAPI_ERROR_NO_MEMORY ; 01058 } 01059 01060 if (_mode == CellularSMSMmodePDU) { 01061 //+CMGL: <index>,<stat>,[<alpha>],<length><CR><LF><pdu>[<CR><LF> 01062 // +CMGL:<index>,<stat>,[<alpha>],<length><CR><LF><pdu> 01063 //[...]] 01064 index = _at.read_int(); 01065 _at.skip_param(2); // <stat>,[<alpha>] 01066 length = _at.read_int(); 01067 length = length*2 + 20;// *2 as it's hex encoded and +20 as service center number is not included in size given by CMGL 01068 pdu = (char*)calloc(length, sizeof(char)); 01069 if (!pdu) { 01070 delete info; 01071 _at.resp_stop(); 01072 return NSAPI_ERROR_NO_MEMORY ; 01073 } 01074 _at.read_string(pdu, length, true); 01075 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 01076 info->msg_size = get_data_from_pdu(pdu, info, &part_number); 01077 } 01078 } else { 01079 // +CMGL: <index>,<stat>,<oa/da>,[<alpha>],[<scts>][,<tooa/toda>,<length>]<CR><LF><data>[<CR><LF> 01080 // +CMGL: <index>,<stat>,<da/oa>,[<alpha>],[<scts>][,<tooa/toda>,<length>]<CR><LF><data>[...]] 01081 index = _at.read_int(); 01082 (void)_at.consume_to_stop_tag(); // consume until <CR><LF> 01083 (void)_at.consume_to_stop_tag(); // consume until <CR><LF> 01084 } 01085 01086 if (index > 0) { 01087 add_info(info, index, part_number); 01088 } else { 01089 delete info; 01090 info = NULL; 01091 } 01092 free(pdu); 01093 pdu = NULL; 01094 } 01095 01096 01097 _at.resp_stop(); 01098 01099 return _at.get_last_error(); 01100 } 01101 01102 AT_CellularSMS::sms_info_t* AT_CellularSMS::get_oldest_sms_index() 01103 { 01104 /* 01105 * Different scenarios when finding the oldest concatenated sms 01106 * 01107 * 1. Find first parts first and it was received first 01108 * 2. Find first parts first and it was NOT received first -> older timestamp might exist in some other part 01109 * 3. Find other than first part first and it was received first 01110 * 4. Find other than first part first and it was NOT received first -> older timestamp might exist in some other part 01111 * 01112 * So must take all message to a linked list and loop that for the oldest 01113 */ 01114 01115 // if text mode we need to read sms with +CMGR because time stamp is optional while looping with +CMGL 01116 sms_info_t* retVal = NULL; 01117 sms_info_t* current = _sms_info; 01118 nsapi_size_or_error_t err = 0; 01119 while (current) { 01120 if (_mode == CellularSMSMmodeText) { 01121 wait_ms(_sim_wait_time); 01122 err = read_sms_from_index(current->msg_index[0], NULL, 0, NULL, current->date); 01123 if (err != 0) { 01124 return NULL; 01125 } 01126 } 01127 01128 if (retVal == NULL) { 01129 retVal = current; 01130 } else if (compare_time_strings(current->date, retVal->date) == -1) { 01131 // found older sms, update return value to oldest 01132 retVal = current; 01133 } 01134 current = current->next_info; 01135 } 01136 01137 return retVal; 01138 } 01139 01140 // if time_string_1 is greater (more fresh date) then return 1, same 0, smaller -1. Error -2 01141 int AT_CellularSMS::compare_time_strings(const char* time_string_1, const char* time_string_2) 01142 { 01143 time_t t1; 01144 time_t t2; 01145 01146 bool success = create_time(time_string_1, &t1) && create_time(time_string_2, &t2); 01147 int retVal = -2; 01148 01149 if (success) { 01150 double diff = difftime(t1, t2); 01151 01152 if (diff > 0) { 01153 retVal = 1; 01154 } else if (diff == 0) { 01155 retVal = 0; 01156 } else { 01157 retVal = -1; 01158 } 01159 } 01160 01161 return retVal; 01162 } 01163 01164 bool AT_CellularSMS::create_time(const char* time_string, time_t* time) 01165 { 01166 const int kNumberOfElements = 8; 01167 tm time_struct = { 0 }; 01168 int gmt = 0; 01169 char sign; 01170 bool retVal = false; 01171 01172 if (sscanf(time_string, "%d/%d/%d,%d:%d:%d%c%d", &time_struct.tm_year, &time_struct.tm_mon, &time_struct.tm_mday, 01173 &time_struct.tm_hour, &time_struct.tm_min, &time_struct.tm_sec, &sign, &gmt) == kNumberOfElements) { 01174 *time = mktime(&time_struct); 01175 // add timezone as seconds. gmt is in quarter of hours. 01176 int x = 60 * 60 * gmt * 0.25; 01177 if (sign == '+') { 01178 *time += x; 01179 } else { 01180 *time -= x; 01181 } 01182 retVal = true; 01183 } 01184 01185 return retVal; 01186 } 01187 01188 uint16_t AT_CellularSMS::pack_7_bit_gsm_and_hex(const char* str, uint16_t len, char *buf, 01189 int number_of_padding_bit) 01190 { 01191 uint16_t strCnt = 0; 01192 uint16_t i = 0; 01193 uint8_t shift; 01194 char tmp; 01195 01196 if (len == 0) { 01197 return 0; 01198 } 01199 // convert to 7bit gsm first 01200 char* gsm_str = (char*)malloc(len); 01201 if (!gsm_str) { 01202 return 0; 01203 } 01204 for (uint16_t y = 0; y < len; y++) { 01205 for (int x=0; x < GSM_TO_ASCII_TABLE_SIZE; x++) { 01206 if (gsm_to_ascii[x] == str[y]) { 01207 gsm_str[y] = x; 01208 } 01209 } 01210 } 01211 01212 // then packing and converting to hex 01213 if (number_of_padding_bit) { 01214 tmp = gsm_str[strCnt]<<number_of_padding_bit; 01215 strCnt++; 01216 char_str_to_hex_str(&tmp, 1, &buf[i*2]); 01217 i++; 01218 } 01219 01220 while (strCnt < len) { 01221 if (number_of_padding_bit) { 01222 shift = (i+number_of_padding_bit-2)%7; 01223 } else { 01224 shift = i%7; 01225 } 01226 01227 if (strCnt+1 == len) { 01228 tmp = (gsm_str[strCnt]>>shift); 01229 } else { 01230 tmp = (gsm_str[strCnt]>>shift) | (gsm_str[strCnt+1] <<(7-shift)); 01231 } 01232 01233 char_str_to_hex_str(&tmp, 1, buf+(i*2)); 01234 01235 if (shift == 6) { 01236 strCnt++; 01237 } 01238 strCnt++; 01239 i++; 01240 } 01241 01242 free(gsm_str); 01243 01244 return i; 01245 } 01246 01247 uint16_t AT_CellularSMS::unpack_7_bit_gsm_to_str(const char* str, int len, char *buf, int padding_bits, 01248 int msg_len) 01249 { 01250 int strCount = 0; 01251 uint16_t decodedCount = 0; 01252 uint8_t shift; 01253 char tmp; 01254 char tmp1; 01255 01256 if (padding_bits) { 01257 hex_str_to_char_str(str, 2, &tmp); 01258 buf[decodedCount] = gsm_to_ascii[(tmp>>padding_bits) & 0x7F]; 01259 strCount++; 01260 decodedCount++; 01261 } 01262 01263 while (strCount < len) { 01264 shift = (strCount-padding_bits)%7; 01265 hex_str_to_char_str(str + strCount*2, 2, &tmp); 01266 if (shift == 0) { 01267 buf[decodedCount] = gsm_to_ascii[tmp & 0x7F]; 01268 } else if (shift == 6) { 01269 hex_str_to_char_str(str + (strCount-1)*2, 2, &tmp1); 01270 buf[decodedCount] = gsm_to_ascii[(((tmp1>>2)) | (tmp << 6)) & 0x7F]; 01271 if (decodedCount+1 < msg_len) { 01272 hex_str_to_char_str(str + strCount*2, 2, &tmp); 01273 decodedCount++; 01274 buf[decodedCount] = gsm_to_ascii[(tmp>>1) & 0x7F]; 01275 } 01276 } else { 01277 hex_str_to_char_str(str + (strCount-1)*2, 2, &tmp1); 01278 buf[decodedCount] = gsm_to_ascii[(((tmp1>>(8- shift))) | ((tmp << shift))) & 0x7F]; 01279 } 01280 01281 strCount++; 01282 decodedCount++; 01283 } 01284 01285 return decodedCount; 01286 }
Generated on Tue Jul 12 2022 18:18:27 by
