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 (NSAPI_ERROR_OK != _at.set_urc_handler("+CMTI:", callback(this, &AT_CellularSMS::cmti_urc)) || 00260 NSAPI_ERROR_OK != _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 } else { 00639 _at.skip_param(); // <oa>,<alpha> 00640 } 00641 _at.skip_param(); // <alpha> 00642 if (time_stamp) { 00643 int len = _at.read_string(time_stamp, SMS_MAX_TIME_STAMP_SIZE); 00644 if (len < (SMS_MAX_TIME_STAMP_SIZE - 2)) { 00645 time_stamp[len++] = ','; 00646 _at.read_string(&time_stamp[len], SMS_MAX_TIME_STAMP_SIZE-len); 00647 } 00648 } 00649 (void)_at.consume_to_stop_tag(); // consume until <CR><LF> 00650 if (buf) { 00651 _at.read_string(buf, len); 00652 buf_len = strlen(buf); 00653 } 00654 } 00655 } 00656 _at.resp_stop(); 00657 } 00658 00659 return (_at.get_last_error() == NSAPI_ERROR_OK ) ? buf_len : _at.get_last_error(); 00660 } 00661 00662 // read msg in PDU mode 00663 nsapi_size_or_error_t AT_CellularSMS::read_sms(sms_info_t *sms, char *buf, char *phone_num, char *time_stamp) 00664 { 00665 // +CMGR: <stat>,[<alpha>],<length><CR><LF><pdu> 00666 int index; 00667 if (sms->parts == sms->parts_added) { 00668 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. 00669 int status; 00670 int msg_len; 00671 index = 0; 00672 int pduSize; 00673 00674 for (int i = 0; i < sms->parts; i++) { 00675 wait_ms(_sim_wait_time); 00676 _at.cmd_start("AT+CMGR="); 00677 _at.write_int(sms->msg_index[i]); 00678 _at.cmd_stop(); 00679 _at.resp_start("+CMGR:"); 00680 00681 if (_at.info_resp()) { 00682 status = _at.read_int(); 00683 _at.skip_param(); // <alpha> 00684 if ((_at.get_last_error() == NSAPI_ERROR_OK ) && (status == 0 || status == 1)) { 00685 msg_len = _at.read_int(); 00686 if (msg_len > 0) { 00687 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 00688 pdu = (char *)calloc(pduSize, sizeof(char)); 00689 if (!pdu) { 00690 _at.resp_stop(); 00691 return NSAPI_ERROR_NO_MEMORY ; 00692 } 00693 _at.read_string(pdu, pduSize, true); 00694 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00695 msg_len = get_data_from_pdu(pdu, NULL, NULL, phone_num, buf + index); 00696 if (msg_len >= 0) { // we need to allow zero length messages 00697 index += msg_len; 00698 } else { 00699 free(pdu); 00700 _at.resp_stop(); 00701 return -1; 00702 } 00703 } 00704 free(pdu); 00705 } 00706 } 00707 } 00708 _at.resp_stop(); 00709 } 00710 00711 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00712 if (time_stamp) { 00713 strcpy(time_stamp, sms->date); 00714 } 00715 buf[index] = '\0'; 00716 } 00717 } else { 00718 tr_warn("NOT all concatenated parts were received..."); 00719 index = SMS_ERROR_MULTIPART_ALL_PARTS_NOT_READ; 00720 } 00721 00722 return index; 00723 } 00724 00725 nsapi_size_or_error_t AT_CellularSMS::get_sms(char *buf, uint16_t len, char *phone_num, uint16_t phone_len, 00726 char *time_stamp, uint16_t time_len, int *buf_size) 00727 { 00728 // validate buffer sizes already here to avoid any necessary function calls and locking of _at 00729 if ((phone_num && phone_len < SMS_MAX_PHONE_NUMBER_SIZE) || (time_stamp && time_len < SMS_MAX_TIME_STAMP_SIZE) || 00730 buf == NULL) { 00731 return NSAPI_ERROR_PARAMETER ; 00732 } 00733 00734 _at.lock(); 00735 00736 nsapi_size_or_error_t err = list_messages(); 00737 if (err == NSAPI_ERROR_OK ) { 00738 // we return the oldest sms and delete it after successful read 00739 sms_info_t *info = get_oldest_sms_index(); 00740 00741 if (info) { 00742 if (info->msg_size > len) { 00743 tr_warn("Given buf too small, len is: %d but is must be: %d", len, info->msg_size); 00744 if (buf_size) { 00745 *buf_size = info->msg_size; 00746 } 00747 free_linked_list(); 00748 _at.unlock(); 00749 return NSAPI_ERROR_PARAMETER ; 00750 } 00751 00752 if (_mode == CellularSMSMmodePDU) { 00753 err = read_sms(info, buf, phone_num, time_stamp); 00754 } else { 00755 err = read_sms_from_index(info->msg_index[0], buf, len, phone_num, time_stamp); 00756 } 00757 00758 if (err > 0) { 00759 int delerr = delete_sms(info); 00760 if (delerr) { 00761 err = delerr; 00762 } 00763 } 00764 } else { 00765 // No messages were found, return -1 00766 err = -1; 00767 } 00768 } 00769 00770 free_linked_list(); 00771 00772 _at.unlock(); 00773 00774 // update error only when there really was an error, otherwise we return the length 00775 if (_at.get_last_error()) { 00776 err = _at.get_last_error(); 00777 } 00778 return err; 00779 } 00780 00781 nsapi_size_or_error_t AT_CellularSMS::get_data_from_pdu(const char *pdu, sms_info_t *info, int *part_number, 00782 char *phone_number, char *msg) 00783 { 00784 int index = 0; 00785 int tmp; 00786 bool userDataHeader; 00787 int oaLength; 00788 int dataScheme; 00789 nsapi_size_or_error_t err = NSAPI_ERROR_OK ; 00790 00791 // read Length of the SMSC information 00792 oaLength = hex_str_to_int(pdu, 2); 00793 index += 2; // length we just read 00794 index += oaLength * 2; // skip service center number 00795 00796 // read first the lower part of first octet as there is message type 00797 index++; 00798 tmp = hex_str_to_int(pdu + index, 1); 00799 //wait_ms(200); 00800 if ((tmp & 0x03) == 0) {// SMS-DELIVER type, last two bits should be zero 00801 // UDH present? Check from first octets higher part 00802 tmp = hex_str_to_int(pdu + (--index), 1); 00803 userDataHeader = ((tmp & 0x04) == 0) ? false : true; 00804 00805 index += 2; // we just read the high bits of first octet so move +2 00806 // originating address length 00807 oaLength = hex_str_to_int(pdu + index, 2); 00808 index += 2; // add index over address length 00809 int type = hex_str_to_int(pdu + index, 1); 00810 index += 2; // add index over type 00811 if (phone_number) { 00812 // phone number as reverse nibble encoded 00813 int a = 0, field_length = oaLength; 00814 00815 if (type == 9) { 00816 //add the plus sign in case of international number (23.040 chapter address fields) 00817 phone_number[a++] = '+'; 00818 field_length++; 00819 } 00820 00821 for (; a < field_length; a += 2) { 00822 if ((a + 1) == field_length) { 00823 phone_number[a] = pdu[index + 1]; 00824 index++; 00825 } else { 00826 phone_number[a] = pdu[index + 1]; 00827 phone_number[a + 1] = pdu[index]; 00828 index += 2; 00829 } 00830 } 00831 phone_number[field_length] = '\0'; 00832 } else { 00833 index += oaLength; 00834 } 00835 00836 00837 if (oaLength & 0x01) { // if phone number length is odd then it has padded F so skip that 00838 index++; 00839 } 00840 index += 2; // skip TP-Protocol identifier 00841 00842 dataScheme = hex_str_to_int(pdu + index, 2); 00843 index += 2; // skip TP-Data-Coding-Scheme 00844 00845 // next one is date, it's length is 7 octets according to 3GPP TS 23.040 00846 // create time string 00847 if (info) { 00848 int i = 0; 00849 // year 00850 info->date[i++] = pdu[index + 1]; 00851 info->date[i++] = pdu[index]; 00852 index += 2; 00853 info->date[i++] = '/'; 00854 // month 00855 info->date[i++] = pdu[index + 1]; 00856 info->date[i++] = pdu[index]; 00857 index += 2; 00858 info->date[i++] = '/'; 00859 // Day 00860 info->date[i++] = pdu[index + 1]; 00861 info->date[i++] = pdu[index]; 00862 index += 2; 00863 info->date[i++] = ','; 00864 // Hour 00865 info->date[i++] = pdu[index + 1]; 00866 info->date[i++] = pdu[index]; 00867 index += 2; 00868 info->date[i++] = ':'; 00869 // Minute 00870 info->date[i++] = pdu[index + 1]; 00871 info->date[i++] = pdu[index]; 00872 index += 2; 00873 info->date[i++] = ':'; 00874 // Second 00875 info->date[i++] = pdu[index + 1]; 00876 info->date[i++] = pdu[index]; 00877 index += 2; 00878 // timezone related to GMT. pdu[index+1] most significant bit indicates the sign related to gmt 00879 tmp = hex_str_to_int(pdu + index + 1, 1); 00880 if (tmp & 0x08) { 00881 info->date[i++] = '-'; 00882 } else { 00883 info->date[i++] = '+'; 00884 } 00885 00886 // pdu[index+1 & 0x07 is the most significant bits of the timezone 00887 // pdu [index] is the least significant bits 00888 info->date[i++] = '0' + (tmp & 0x07); 00889 info->date[i++] = pdu[index]; 00890 info->date[i] = '\0'; 00891 index += 2; 00892 } else { 00893 index += 14; 00894 } 00895 00896 int udl = hex_str_to_int(pdu + index, 2); 00897 index += 2; 00898 00899 int paddingBits = 0; 00900 int partnro = 1; 00901 if (userDataHeader) { 00902 // we need to read User Defined Header to know what part number this message is. 00903 index += read_udh_from_pdu(pdu + index, info, partnro, paddingBits); 00904 } 00905 00906 if (part_number) { 00907 *part_number = partnro; 00908 } 00909 00910 if (msg) { 00911 // we are reading the message 00912 err = read_pdu_payload(pdu + index, udl, dataScheme, msg, paddingBits); 00913 } else { 00914 if (dataScheme == 0x00) { 00915 // when listing messages we need to calculated length. Other way would be unpacking the whole message. 00916 err = strlen(pdu + index) >> 1; 00917 err *= 8; 00918 err /= 7; 00919 } else if (dataScheme == 0x04) { 00920 err = strlen(pdu + index) >> 1; 00921 } else { 00922 return NSAPI_ERROR_UNSUPPORTED ; 00923 } 00924 } 00925 00926 return err; 00927 } else { 00928 // message was not DELIVER so discard it 00929 return NSAPI_ERROR_UNSUPPORTED ; 00930 } 00931 } 00932 00933 // read params from User Defined Header 00934 int AT_CellularSMS::read_udh_from_pdu(const char *pdu, sms_info_t *info, int &part_number, int &padding_bits) 00935 { 00936 00937 int index = 0; 00938 int udhLength = hex_str_to_int(pdu, 2); 00939 index += 2; 00940 00941 // if there is padding bits then udhlen is octet bigger as we need to keep septet boundary 00942 padding_bits = ((udhLength + 1) * 8) % 7; // +1 is for udhLength itself 00943 00944 if (padding_bits) { 00945 padding_bits = 7 - padding_bits; 00946 } else { 00947 padding_bits = 0; 00948 } 00949 00950 int tmp = hex_str_to_int(pdu + index, 2); 00951 index += 4; 00952 00953 if (tmp == 0) { // 8-bit reference number 00954 if (info) { 00955 info->msg_ref_number = (uint16_t)hex_str_to_int(pdu + index, 2); 00956 } 00957 index += 2; 00958 } else { // 16-bit reference number 00959 if (info) { 00960 info->msg_ref_number = (uint16_t)hex_str_to_int(pdu + index + 2, 2); 00961 tmp = hex_str_to_int(pdu + index, 2); 00962 info->msg_ref_number |= (tmp << 8); 00963 } 00964 index += 4; 00965 } 00966 00967 if (info) { 00968 info->parts = hex_str_to_int(pdu + index, 2); 00969 } 00970 index += 2; 00971 00972 part_number = hex_str_to_int(pdu + index, 2); 00973 index += 2; 00974 00975 return (udhLength * 2 + 2); // udh in hex and udhl 00976 } 00977 00978 nsapi_size_or_error_t AT_CellularSMS::read_pdu_payload(const char *pdu, int msg_len, int scheme, char *msg, int padding_bits) 00979 { 00980 if (scheme == 0x00) { 00981 // 7 bit gsm encoding, must do the conversions from hex to 7-bit encoding and to ascii 00982 return unpack_7_bit_gsm_to_str(pdu, strlen(pdu) / 2, msg, padding_bits, msg_len); 00983 } else if (scheme == 0x04) { 00984 // 8bit scheme so just convert hexstring to charstring 00985 return hex_str_to_char_str(pdu, strlen(pdu), msg); 00986 } else { 00987 tr_error("Received unsupported data coding scheme: 0x%02x", scheme); 00988 return NSAPI_ERROR_UNSUPPORTED ; 00989 } 00990 } 00991 00992 void AT_CellularSMS::free_linked_list() 00993 { 00994 sms_info_t *info = _sms_info; 00995 sms_info_t *old; 00996 while (info) { 00997 old = info; 00998 info = info->next_info; 00999 delete old; 01000 } 01001 _sms_info = NULL; 01002 } 01003 01004 void AT_CellularSMS::add_info(sms_info_t *info, int index, int part_number) 01005 { 01006 // check for same message reference id. If found, update it and delete the given info. 01007 // if NOT found then add to the end of the list. 01008 01009 if (!_sms_info) { 01010 info->msg_index[part_number - 1] = index; // part numbering starts from 1 so -1 to put to right index 01011 _sms_info = info; 01012 return; 01013 } 01014 sms_info_t *current = _sms_info; 01015 sms_info_t *prev; 01016 bool found_msg = false; 01017 while (current) { 01018 prev = current; 01019 // sms messages can have same reference number so additional checks are needed. 01020 // TODO: should we include phone number also? 01021 if (current->msg_ref_number == info->msg_ref_number && current->parts > current->parts_added && 01022 info->parts > info->parts_added) { 01023 // multipart sms, update msg size and index 01024 current->msg_size += info->msg_size; 01025 current->msg_index[part_number - 1] = index; // part numbering starts from 1 so -1 to put to right index 01026 current->parts_added++; 01027 // update oldest part as date 01028 if (compare_time_strings(info->date, current->date) == -1) { 01029 strcpy(current->date, info->date); 01030 } 01031 found_msg = true; 01032 break; 01033 } 01034 current = current->next_info; 01035 } 01036 01037 if (found_msg) { 01038 // info was added to existing item in linked list, must be deleted 01039 delete info; 01040 } else { 01041 // message not found, add to linked list 01042 info->msg_index[part_number - 1] = index; 01043 prev->next_info = info; 01044 } 01045 } 01046 01047 // reads all the messages to the linked list AT_CellularSMS::_sms_info 01048 nsapi_error_t AT_CellularSMS::list_messages() 01049 { 01050 // TODO: NOTE: If the selected <mem1> can contain different types of SMs (e.g. SMS-DELIVERs, SMS-SUBMITs, SMS-STATUS-REPORTs and SMS-COMMANDs), 01051 // 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. 01052 // for now we assume that only SMS-DELIVER messages are read. 01053 if (_mode == CellularSMSMmodePDU) { 01054 _at.cmd_start("AT+CMGL=4"); 01055 } else { 01056 _at.cmd_start("AT+CMGL=\"ALL\""); 01057 } 01058 _at.cmd_stop(); 01059 01060 sms_info_t *info = NULL; 01061 // init for 1 so that in text mode we will add to the correct place without any additional logic in addInfo() in text mode 01062 int part_number = 1; 01063 int index = 0; 01064 int length = 0; 01065 char *pdu = NULL; 01066 01067 _at.resp_start("+CMGL:"); 01068 while (_at.info_resp()) { 01069 info = new sms_info_t(); 01070 if (!info) { 01071 _at.resp_stop(); 01072 return NSAPI_ERROR_NO_MEMORY ; 01073 } 01074 01075 if (_mode == CellularSMSMmodePDU) { 01076 //+CMGL: <index>,<stat>,[<alpha>],<length><CR><LF><pdu>[<CR><LF> 01077 // +CMGL:<index>,<stat>,[<alpha>],<length><CR><LF><pdu> 01078 //[...]] 01079 index = _at.read_int(); 01080 _at.skip_param(2); // <stat>,[<alpha>] 01081 length = _at.read_int(); 01082 length = length * 2 + 20; // *2 as it's hex encoded and +20 as service center number is not included in size given by CMGL 01083 pdu = (char *)calloc(length, sizeof(char)); 01084 if (!pdu) { 01085 delete info; 01086 _at.resp_stop(); 01087 return NSAPI_ERROR_NO_MEMORY ; 01088 } 01089 _at.read_string(pdu, length, true); 01090 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 01091 info->msg_size = get_data_from_pdu(pdu, info, &part_number); 01092 } 01093 } else { 01094 // +CMGL: <index>,<stat>,<oa/da>,[<alpha>],[<scts>][,<tooa/toda>,<length>]<CR><LF><data>[<CR><LF> 01095 // +CMGL: <index>,<stat>,<da/oa>,[<alpha>],[<scts>][,<tooa/toda>,<length>]<CR><LF><data>[...]] 01096 index = _at.read_int(); 01097 (void)_at.consume_to_stop_tag(); // consume until <CR><LF> 01098 (void)_at.consume_to_stop_tag(); // consume until <CR><LF> 01099 } 01100 01101 if (index > 0) { 01102 add_info(info, index, part_number); 01103 } else { 01104 delete info; 01105 info = NULL; 01106 } 01107 free(pdu); 01108 pdu = NULL; 01109 } 01110 01111 01112 _at.resp_stop(); 01113 01114 return _at.get_last_error(); 01115 } 01116 01117 AT_CellularSMS::sms_info_t *AT_CellularSMS::get_oldest_sms_index() 01118 { 01119 /* 01120 * Different scenarios when finding the oldest concatenated sms 01121 * 01122 * 1. Find first parts first and it was received first 01123 * 2. Find first parts first and it was NOT received first -> older timestamp might exist in some other part 01124 * 3. Find other than first part first and it was received first 01125 * 4. Find other than first part first and it was NOT received first -> older timestamp might exist in some other part 01126 * 01127 * So must take all message to a linked list and loop that for the oldest 01128 */ 01129 01130 // if text mode we need to read sms with +CMGR because time stamp is optional while looping with +CMGL 01131 sms_info_t *retVal = NULL; 01132 sms_info_t *current = _sms_info; 01133 nsapi_size_or_error_t err = 0; 01134 while (current) { 01135 if (_mode == CellularSMSMmodeText) { 01136 wait_ms(_sim_wait_time); 01137 err = read_sms_from_index(current->msg_index[0], NULL, 0, NULL, current->date); 01138 if (err != 0) { 01139 return NULL; 01140 } 01141 } 01142 01143 if (retVal == NULL) { 01144 retVal = current; 01145 } else if (compare_time_strings(current->date, retVal->date) == -1) { 01146 // found older sms, update return value to oldest 01147 retVal = current; 01148 } 01149 current = current->next_info; 01150 } 01151 01152 return retVal; 01153 } 01154 01155 // if time_string_1 is greater (more fresh date) then return 1, same 0, smaller -1. Error -2 01156 int AT_CellularSMS::compare_time_strings(const char *time_string_1, const char *time_string_2) 01157 { 01158 time_t t1; 01159 time_t t2; 01160 01161 bool success = create_time(time_string_1, &t1) && create_time(time_string_2, &t2); 01162 int retVal = -2; 01163 01164 if (success) { 01165 double diff = difftime(t1, t2); 01166 01167 if (diff > 0) { 01168 retVal = 1; 01169 } else if (diff == 0) { 01170 retVal = 0; 01171 } else { 01172 retVal = -1; 01173 } 01174 } 01175 01176 return retVal; 01177 } 01178 01179 bool AT_CellularSMS::create_time(const char *time_string, time_t *time) 01180 { 01181 const int kNumberOfElements = 8; 01182 tm time_struct = { 0 }; 01183 int gmt = 0; 01184 char sign; 01185 bool retVal = false; 01186 01187 if (sscanf(time_string, "%d/%d/%d,%d:%d:%d%c%d", &time_struct.tm_year, &time_struct.tm_mon, &time_struct.tm_mday, 01188 &time_struct.tm_hour, &time_struct.tm_min, &time_struct.tm_sec, &sign, &gmt) == kNumberOfElements) { 01189 *time = mktime(&time_struct); 01190 // add timezone as seconds. gmt is in quarter of hours. 01191 int x = 60 * 60 * gmt * 0.25; 01192 if (sign == '+') { 01193 *time += x; 01194 } else { 01195 *time -= x; 01196 } 01197 retVal = true; 01198 } 01199 01200 return retVal; 01201 } 01202 01203 uint16_t AT_CellularSMS::pack_7_bit_gsm_and_hex(const char *str, uint16_t len, char *buf, 01204 int number_of_padding_bit) 01205 { 01206 uint16_t strCnt = 0; 01207 uint16_t i = 0; 01208 uint8_t shift; 01209 char tmp; 01210 01211 if (len == 0) { 01212 return 0; 01213 } 01214 // convert to 7bit gsm first 01215 char *gsm_str = (char *)malloc(len); 01216 if (!gsm_str) { 01217 return 0; 01218 } 01219 for (uint16_t y = 0; y < len; y++) { 01220 for (int x = 0; x < GSM_TO_ASCII_TABLE_SIZE; x++) { 01221 if (gsm_to_ascii[x] == str[y]) { 01222 gsm_str[y] = x; 01223 } 01224 } 01225 } 01226 01227 // then packing and converting to hex 01228 if (number_of_padding_bit) { 01229 tmp = gsm_str[strCnt] << number_of_padding_bit; 01230 strCnt++; 01231 char_str_to_hex_str(&tmp, 1, &buf[i * 2]); 01232 i++; 01233 } 01234 01235 while (strCnt < len) { 01236 if (number_of_padding_bit) { 01237 shift = (i + number_of_padding_bit - 2) % 7; 01238 } else { 01239 shift = i % 7; 01240 } 01241 01242 if (strCnt + 1 == len) { 01243 tmp = (gsm_str[strCnt] >> shift); 01244 } else { 01245 tmp = (gsm_str[strCnt] >> shift) | (gsm_str[strCnt + 1] << (7 - shift)); 01246 } 01247 01248 char_str_to_hex_str(&tmp, 1, buf + (i * 2)); 01249 01250 if (shift == 6) { 01251 strCnt++; 01252 } 01253 strCnt++; 01254 i++; 01255 } 01256 01257 free(gsm_str); 01258 01259 return i; 01260 } 01261 01262 uint16_t AT_CellularSMS::unpack_7_bit_gsm_to_str(const char *str, int len, char *buf, int padding_bits, 01263 int msg_len) 01264 { 01265 int strCount = 0; 01266 uint16_t decodedCount = 0; 01267 uint8_t shift; 01268 char tmp; 01269 char tmp1; 01270 01271 if (padding_bits) { 01272 hex_str_to_char_str(str, 2, &tmp); 01273 buf[decodedCount] = gsm_to_ascii[(tmp >> padding_bits) & 0x7F]; 01274 strCount++; 01275 decodedCount++; 01276 } 01277 01278 while (strCount < len) { 01279 shift = (strCount - padding_bits) % 7; 01280 hex_str_to_char_str(str + strCount * 2, 2, &tmp); 01281 if (shift == 0) { 01282 buf[decodedCount] = gsm_to_ascii[tmp & 0x7F]; 01283 } else if (shift == 6) { 01284 hex_str_to_char_str(str + (strCount - 1) * 2, 2, &tmp1); 01285 buf[decodedCount] = gsm_to_ascii[(((tmp1 >> 2)) | (tmp << 6)) & 0x7F]; 01286 if (decodedCount + 1 < msg_len) { 01287 hex_str_to_char_str(str + strCount * 2, 2, &tmp); 01288 decodedCount++; 01289 buf[decodedCount] = gsm_to_ascii[(tmp >> 1) & 0x7F]; 01290 } 01291 } else { 01292 hex_str_to_char_str(str + (strCount - 1) * 2, 2, &tmp1); 01293 buf[decodedCount] = gsm_to_ascii[(((tmp1 >> (8 - shift))) | ((tmp << shift))) & 0x7F]; 01294 } 01295 01296 strCount++; 01297 decodedCount++; 01298 } 01299 01300 return decodedCount; 01301 }
Generated on Tue Aug 9 2022 00:37:03 by
