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