Parser for AT commands and similar protocols
Dependencies: BufferedSerial
Dependents: ESP8266 xdot-passthru Lab_10 Lab9 ... more
Fork of ATParser by
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 }
Generated on Sun Jul 17 2022 04:40:46 by 1.7.2