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