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