ATParser for X-NUCLEO-IDW01M1 module

Dependencies:   BufferedSerial

Dependents:   SPWF01SA-lapi-1 SPWF01SA Nucleo-AWS-IoT-mbed

Fork of ATParser by ST Expansion SW Team

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATParser.cpp Source File

ATParser.cpp

00001 /* Copyright (c) 2015 ARM Limited
00002  *
00003  * Licensed under the Apache License, Version 2.0 (the "License");
00004  * you may not use this file except in compliance with the License.
00005  * You may obtain a copy of the License at
00006  *
00007  *     http://www.apache.org/licenses/LICENSE-2.0
00008  *
00009  * Unless required by applicable law or agreed to in writing, software
00010  * distributed under the License is distributed on an "AS IS" BASIS,
00011  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012  * See the License for the specific language governing permissions and
00013  * limitations under the License.
00014  *
00015  * @section DESCRIPTION
00016  *
00017  * Parser for the AT command syntax
00018  *
00019  */
00020 
00021 #include "ATParser.h"
00022 #include "mbed_debug.h"
00023 
00024 
00025 // getc/putc handling with timeouts
00026 int ATParser::putc(char c)
00027 {
00028     Timer timer;
00029     timer.start();
00030 
00031     while (true) {
00032         if (_serial->writeable()) {
00033             return _serial->putc(c);
00034         }
00035         if (timer.read_ms() > _timeout) {
00036             return -1;
00037         }
00038     }
00039 }
00040 
00041 int ATParser::getc()
00042 {
00043     Timer timer;
00044     timer.start();
00045 
00046     while (true) {
00047         if (_serial->readable()) {
00048             return _serial->getc();
00049         }
00050         if (timer.read_ms() > _timeout) {
00051             return -1;
00052         }
00053     }
00054 }
00055 
00056 void ATParser::flush()
00057 {
00058     while (_serial->readable()) {
00059         _serial->getc();
00060     }
00061 }
00062 
00063 
00064 // read/write handling with timeouts
00065 int ATParser::write(const char *data, int size)
00066 {
00067     int i = 0;
00068     for ( ; i < size; i++) {
00069         if (putc(data[i]) < 0) {
00070             return -1;
00071         }
00072     }
00073     return i;
00074 }
00075 
00076 int ATParser::read(char *data, int size)
00077 {
00078     int i = 0;
00079     for ( ; i < size; i++) {
00080         int c = getc();
00081         if (c < 0) {
00082             return -1;
00083         }
00084         data[i] = c;
00085     }
00086     return i;
00087 }
00088 
00089 
00090 // printf/scanf handling
00091 int ATParser::vprintf(const char *format, va_list args)
00092 {
00093     if (vsprintf(_buffer, format, args) < 0) {
00094         return false;
00095     }
00096     int i = 0;
00097     for ( ; _buffer[i]; i++) {
00098         if (putc(_buffer[i]) < 0) {
00099             return -1;
00100         }
00101     }
00102     return i;
00103 }
00104 
00105 int ATParser::vscanf(const char *format, va_list args)
00106 {
00107     // Since format is const, we need to copy it into our buffer to
00108     // add the line's null terminator and clobber value-matches with asterisks.
00109     //
00110     // We just use the beginning of the buffer to avoid unnecessary allocations.
00111     int i = 0;
00112     int offset = 0;
00113 
00114     while (format[i]) {
00115         if (format[i] == '%' && format[i+1] != '%' && format[i+1] != '*') {
00116             _buffer[offset++] = '%';
00117             _buffer[offset++] = '*';
00118             i++;
00119         } else {
00120             _buffer[offset++] = format[i++];
00121         }
00122     }
00123 
00124     // Scanf has very poor support for catching errors
00125     // fortunately, we can abuse the %n specifier to determine
00126     // if the entire string was matched.
00127     _buffer[offset++] = '%';
00128     _buffer[offset++] = 'n';
00129     _buffer[offset++] = 0;
00130 
00131     // To workaround scanf's lack of error reporting, we actually
00132     // make two passes. One checks the validity with the modified
00133     // format string that only stores the matched characters (%n).
00134     // The other reads in the actual matched values.
00135     //
00136     // We keep trying the match until we succeed or some other error
00137     // derails us.
00138     int j = 0;
00139 
00140     while (true) {
00141         // Ran out of space
00142         if (j+1 >= _buffer_size - offset) {
00143             return false;
00144         }
00145         // Recieve next character
00146         int c = getc();
00147         if (c < 0) {
00148             return -1;
00149         }
00150         _buffer[offset + j++] = c;
00151         _buffer[offset + j] = 0;
00152 
00153         // Check for match
00154         int count = -1;
00155         sscanf(_buffer+offset, _buffer, &count);
00156 
00157         // We only succeed if all characters in the response are matched
00158         if (count == j) {
00159             // Store the found results
00160             vsscanf(_buffer+offset, format, args);
00161             return j;
00162         }
00163     }
00164 }
00165 
00166 
00167 // Command parsing with line handling
00168 bool ATParser::vsend(const char *command, va_list args)
00169 {
00170     // Create and send command
00171     if (vsprintf(_buffer, command, args) < 0) {
00172         return false;
00173     }
00174     for (int i = 0; _buffer[i]; i++) {
00175         if (putc(_buffer[i]) < 0) {
00176             return false;
00177         }
00178     }
00179 
00180     // Finish with CR "\r"
00181     //for (int i = 0; _delimiter[i]; i++) {
00182         if (putc(_delimiter[0]) < 0) {
00183             return false;
00184         }
00185     //}
00186 
00187     debug_if(dbg_on, "AT> %s\r\n", _buffer);
00188     return true;
00189 }
00190 
00191 bool ATParser::vrecv(const char *response, va_list args)
00192 {
00193     bool delim_recv = false;
00194     // Iterate through each line in the expected response
00195     while (response[0]) {
00196         // Since response is const, we need to copy it into our buffer to
00197         // add the line's null terminator and clobber value-matches with asterisks.
00198         //
00199         // We just use the beginning of the buffer to avoid unnecessary allocations.
00200         int i = 0;
00201         int offset = 0;
00202 
00203         while (response[i]) {
00204             if (memcmp(&response[i+1-_delim_size], _delimiter, _delim_size) == 0) {
00205                 i++;
00206                 break;
00207             } else if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') {
00208                 _buffer[offset++] = '%';
00209                 _buffer[offset++] = '*';
00210                 i++;
00211             } else {
00212                 _buffer[offset++] = response[i++];
00213             }
00214         }
00215 
00216         // Scanf has very poor support for catching errors
00217         // fortunately, we can abuse the %n specifier to determine
00218         // if the entire string was matched.
00219         _buffer[offset++] = '%';
00220         _buffer[offset++] = 'n';
00221         _buffer[offset++] = 0;
00222 
00223         // To workaround scanf's lack of error reporting, we actually
00224         // make two passes. One checks the validity with the modified
00225         // format string that only stores the matched characters (%n).
00226         // The other reads in the actual matched values.
00227         //
00228         // We keep trying the match until we succeed or some other error
00229         // derails us.
00230         int j = 0;
00231         
00232         while (true) {
00233             // Receive next character
00234             int c = getc();
00235             if (c < 0) {
00236                 return false;
00237             }
00238             _buffer[offset + j++] = c;
00239             _buffer[offset + j] = 0;
00240 
00241             // Check for match
00242             int count = -1;
00243             sscanf(_buffer+offset, _buffer, &count);
00244             
00245             // We only succeed if all characters in the response are matched
00246             if(count==j) {
00247                 //check if last input is a number
00248                 if (_buffer[offset-6]=='%' && _buffer[offset-5]=='*' 
00249                     && (_buffer[offset-4]=='x' || _buffer[offset-4]=='d' || _buffer[offset-4]=='u'))
00250                 {
00251                     //if the last char is a number, keep getting the next character till CR
00252                     while(true)
00253                     {
00254                         int c = getc();
00255                         if (c < 0) {
00256                             return false;
00257                         }
00258                         if(c==0xD) {//there is no next number so exit from condition   
00259                             c = getc();//get rid of the following '\n' delimiter
00260                             delim_recv = true;
00261                             break;
00262                         }
00263                         else {
00264                             _buffer[offset + j++] = c;
00265                             _buffer[offset + j] = 0;
00266                         }
00267                     }
00268                 }
00269                     
00270                 debug_if(dbg_on, "AT= %s\r\n", _buffer+offset);
00271                 // Reuse the front end of the buffer
00272                 memcpy(_buffer, response, i);
00273                 _buffer[i] = 0;
00274 
00275                 // Store the found results
00276                 vsscanf(_buffer+offset, _buffer, args);
00277 
00278                 // Jump to next line and continue parsing
00279                 response += i;
00280                 
00281                 // receive trailing delimiters
00282                  for(int i=0; !delim_recv && i<_delim_size; i++) {
00283                     c = getc();
00284                     if(c < 0)
00285                         break;
00286                 }
00287                 break;
00288             }
00289 
00290             // Clear the buffer when we hit a newline or ran out of space
00291             // running out of space usually means we ran into binary data
00292             if (j+1 >= _buffer_size - offset ||
00293                 strcmp(&_buffer[offset + j-_delim_size], _delimiter) == 0) {
00294 
00295                 debug_if(dbg_on, "AT< %s", _buffer+offset);
00296                 j = 0;
00297             }
00298         }
00299     }
00300 
00301     return true;
00302 }
00303 
00304 
00305 // Mapping to vararg functions
00306 int ATParser::printf(const char *format, ...)
00307 {
00308     va_list args;
00309     va_start(args, format);
00310     int res = vprintf(format, args);
00311     va_end(args);
00312     return res;
00313 }
00314 
00315 int ATParser::scanf(const char *format, ...)
00316 {
00317     va_list args;
00318     va_start(args, format);
00319     int res = vscanf(format, args);
00320     va_end(args);
00321     return res;
00322 }
00323 
00324 bool ATParser::send(const char *command, ...)
00325 {
00326     va_list args;
00327     va_start(args, command);
00328     bool res = vsend(command, args);
00329     va_end(args);
00330     return res;
00331 }
00332 
00333 bool ATParser::recv(const char *response, ...)
00334 {
00335     va_list args;
00336     va_start(args, response);
00337     bool res = vrecv(response, args);
00338     va_end(args);
00339     return res;
00340 }