Version of easy-connect with the u-blox cellular platforms C027 and C030 added.

Dependents:   HelloMQTT

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 vrecv_start:
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             // Recieve 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 oob data
00242             for (int k = 0; k < _oobs.size(); k++) {
00243                 if (j == _oobs[k].len && memcmp(
00244                         _oobs[k].prefix, _buffer+offset, _oobs[k].len) == 0) {
00245                     debug_if(dbg_on, "AT! %s\r\n", _oobs[k].prefix);
00246                     _oobs[k].cb();
00247 
00248                     // oob may have corrupted non-reentrant buffer,
00249                     // so we need to set it up again.
00250                     // Use goto to save stack usage rather than a
00251                     // recursive approach.
00252                     goto vrecv_start;
00253                 }
00254             }
00255 
00256             // Check for match
00257             int count = -1;
00258             sscanf(_buffer+offset, _buffer, &count);
00259 
00260             // We only succeed if all characters in the response are matched
00261             if (count == j) {
00262                 debug_if(dbg_on, "AT= %s\r\n", _buffer+offset);
00263                 // Reuse the front end of the buffer
00264                 memcpy(_buffer, response, i);
00265                 _buffer[i] = 0;
00266 
00267                 // Store the found results
00268                 vsscanf(_buffer+offset, _buffer, args);
00269 
00270                 // Jump to next line and continue parsing
00271                 response += i;
00272                 break;
00273             }
00274 
00275             // Clear the buffer when we hit a newline or ran out of space
00276             // running out of space usually means we ran into binary data
00277             if (j+1 >= _buffer_size - offset ||
00278                 strcmp(&_buffer[offset + j-_delim_size], _delimiter) == 0) {
00279 
00280                 debug_if(dbg_on, "AT< %s", _buffer+offset);
00281                 j = 0;
00282             }
00283         }
00284     }
00285 
00286     return true;
00287 }
00288 
00289 
00290 // Mapping to vararg functions
00291 int ATParser::printf(const char *format, ...)
00292 {
00293     va_list args;
00294     va_start(args, format);
00295     int res = vprintf(format, args);
00296     va_end(args);
00297     return res;
00298 }
00299 
00300 int ATParser::scanf(const char *format, ...)
00301 {
00302     va_list args;
00303     va_start(args, format);
00304     int res = vscanf(format, args);
00305     va_end(args);
00306     return res;
00307 }
00308 
00309 bool ATParser::send(const char *command, ...)
00310 {
00311     va_list args;
00312     va_start(args, command);
00313     bool res = vsend(command, args);
00314     va_end(args);
00315     return res;
00316 }
00317 
00318 bool ATParser::recv(const char *response, ...)
00319 {
00320     va_list args;
00321     va_start(args, response);
00322     bool res = vrecv(response, args);
00323     va_end(args);
00324     return res;
00325 }
00326 
00327 
00328 // oob registration
00329 void ATParser::oob(const char *prefix, Callback<void()> cb)
00330 {
00331     struct oob oob;
00332     oob.len = strlen(prefix);
00333     oob.prefix = prefix;
00334     oob.cb = cb;
00335     _oobs.push_back(oob);
00336 }