Knight KE / Mbed OS Game_Master
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers AT_CellularSMS.cpp Source File

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 }