Gleb Klochkov / Mbed OS Climatcontroll_Main

Dependencies:   esp8266-driver

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