Parser for AT commands and similar protocols

Dependencies:   BufferedSerial

Dependents:   ESP8266 xdot-passthru Lab_10 Lab9 ... more

Fork of ATParser by NetworkSocketAPI

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 newline
00181     for (int i = 0; _delimiter[i]; i++) {
00182         if (putc(_delimiter[i]) < 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     // Iterate through each line in the expected response
00194     while (response[0]) {
00195         // Since response is const, we need to copy it into our buffer to
00196         // add the line's null terminator and clobber value-matches with asterisks.
00197         //
00198         // We just use the beginning of the buffer to avoid unnecessary allocations.
00199         int i = 0;
00200         int offset = 0;
00201 
00202         while (response[i]) {
00203             if (memcmp(&response[i+1-_delim_size], _delimiter, _delim_size) == 0) {
00204                 i++;
00205                 break;
00206             } else if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') {
00207                 _buffer[offset++] = '%';
00208                 _buffer[offset++] = '*';
00209                 i++;
00210             } else {
00211                 _buffer[offset++] = response[i++];
00212             }
00213         }
00214 
00215         // Scanf has very poor support for catching errors
00216         // fortunately, we can abuse the %n specifier to determine
00217         // if the entire string was matched.
00218         _buffer[offset++] = '%';
00219         _buffer[offset++] = 'n';
00220         _buffer[offset++] = 0;
00221 
00222         // To workaround scanf's lack of error reporting, we actually
00223         // make two passes. One checks the validity with the modified
00224         // format string that only stores the matched characters (%n).
00225         // The other reads in the actual matched values.
00226         //
00227         // We keep trying the match until we succeed or some other error
00228         // derails us.
00229         int j = 0;
00230 
00231         while (true) {
00232             // Recieve next character
00233             int c = getc();
00234             if (c < 0) {
00235                 return false;
00236             }
00237             _buffer[offset + j++] = c;
00238             _buffer[offset + j] = 0;
00239 
00240             // Check for match
00241             int count = -1;
00242             sscanf(_buffer+offset, _buffer, &count);
00243 
00244             // We only succeed if all characters in the response are matched
00245             if (count == j) {
00246                 debug_if(dbg_on, "AT= %s\r\n", _buffer+offset);
00247                 // Reuse the front end of the buffer
00248                 memcpy(_buffer, response, i);
00249                 _buffer[i] = 0;
00250 
00251                 // Store the found results
00252                 vsscanf(_buffer+offset, _buffer, args);
00253 
00254                 // Jump to next line and continue parsing
00255                 response += i;
00256                 break;
00257             }
00258 
00259             // Clear the buffer when we hit a newline or ran out of space
00260             // running out of space usually means we ran into binary data
00261             if (j+1 >= _buffer_size - offset ||
00262                 strcmp(&_buffer[offset + j-_delim_size], _delimiter) == 0) {
00263 
00264                 debug_if(dbg_on, "AT< %s", _buffer+offset);
00265                 j = 0;
00266             }
00267         }
00268     }
00269 
00270     return true;
00271 }
00272 
00273 
00274 // Mapping to vararg functions
00275 int ATParser::printf(const char *format, ...)
00276 {
00277     va_list args;
00278     va_start(args, format);
00279     int res = vprintf(format, args);
00280     va_end(args);
00281     return res;
00282 }
00283 
00284 int ATParser::scanf(const char *format, ...)
00285 {
00286     va_list args;
00287     va_start(args, format);
00288     int res = vscanf(format, args);
00289     va_end(args);
00290     return res;
00291 }
00292 
00293 bool ATParser::send(const char *command, ...)
00294 {
00295     va_list args;
00296     va_start(args, command);
00297     bool res = vsend(command, args);
00298     va_end(args);
00299     return res;
00300 }
00301 
00302 bool ATParser::recv(const char *response, ...)
00303 {
00304     va_list args;
00305     va_start(args, response);
00306     bool res = vrecv(response, args);
00307     va_end(args);
00308     return res;
00309 }