Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: nRF51_Vdd TextLCD BME280
ATCmdParser.cpp
00001 /* Copyright (c) 2017 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 "ATCmdParser.h" 00022 #include "mbed_poll.h" 00023 #include "mbed_debug.h" 00024 00025 #ifdef LF 00026 #undef LF 00027 #define LF 10 00028 #else 00029 #define LF 10 00030 #endif 00031 00032 #ifdef CR 00033 #undef CR 00034 #define CR 13 00035 #else 00036 #define CR 13 00037 #endif 00038 00039 // getc/putc handling with timeouts 00040 int ATCmdParser::putc(char c) 00041 { 00042 pollfh fhs; 00043 fhs.fh = _fh; 00044 fhs.events = POLLOUT; 00045 00046 int count = poll(&fhs, 1, _timeout); 00047 if (count > 0 && (fhs.revents & POLLOUT)) { 00048 return _fh->write(&c, 1) == 1 ? 0 : -1; 00049 } else { 00050 return -1; 00051 } 00052 } 00053 00054 int ATCmdParser::getc() 00055 { 00056 pollfh fhs; 00057 fhs.fh = _fh; 00058 fhs.events = POLLIN; 00059 00060 int count = poll(&fhs, 1, _timeout); 00061 if (count > 0 && (fhs.revents & POLLIN)) { 00062 unsigned char ch; 00063 return _fh->read(&ch, 1) == 1 ? ch : -1; 00064 } else { 00065 return -1; 00066 } 00067 } 00068 00069 void ATCmdParser::flush() 00070 { 00071 while (_fh->readable()) { 00072 unsigned char ch; 00073 _fh->read(&ch, 1); 00074 } 00075 } 00076 00077 00078 // read/write handling with timeouts 00079 int ATCmdParser::write(const char *data, int size) 00080 { 00081 int i = 0; 00082 for (; i < size; i++) { 00083 if (putc(data[i]) < 0) { 00084 return -1; 00085 } 00086 } 00087 return i; 00088 } 00089 00090 int ATCmdParser::read(char *data, int size) 00091 { 00092 int i = 0; 00093 for (; i < size; i++) { 00094 int c = getc(); 00095 if (c < 0) { 00096 return -1; 00097 } 00098 data[i] = c; 00099 } 00100 return i; 00101 } 00102 00103 00104 // printf/scanf handling 00105 int ATCmdParser::vprintf(const char *format, va_list args) 00106 { 00107 00108 if (vsprintf(_buffer, format, args) < 0) { 00109 return false; 00110 } 00111 00112 int i = 0; 00113 for (; _buffer[i]; i++) { 00114 if (putc(_buffer[i]) < 0) { 00115 return -1; 00116 } 00117 } 00118 return i; 00119 } 00120 00121 int ATCmdParser::vscanf(const char *format, va_list args) 00122 { 00123 // Since format is const, we need to copy it into our buffer to 00124 // add the line's null terminator and clobber value-matches with asterisks. 00125 // 00126 // We just use the beginning of the buffer to avoid unnecessary allocations. 00127 int i = 0; 00128 int offset = 0; 00129 00130 while (format[i]) { 00131 if (format[i] == '%' && format[i + 1] != '%' && format[i + 1] != '*') { 00132 _buffer[offset++] = '%'; 00133 _buffer[offset++] = '*'; 00134 i++; 00135 } else { 00136 _buffer[offset++] = format[i++]; 00137 } 00138 } 00139 00140 // Scanf has very poor support for catching errors 00141 // fortunately, we can abuse the %n specifier to determine 00142 // if the entire string was matched. 00143 _buffer[offset++] = '%'; 00144 _buffer[offset++] = 'n'; 00145 _buffer[offset++] = 0; 00146 00147 // To workaround scanf's lack of error reporting, we actually 00148 // make two passes. One checks the validity with the modified 00149 // format string that only stores the matched characters (%n). 00150 // The other reads in the actual matched values. 00151 // 00152 // We keep trying the match until we succeed or some other error 00153 // derails us. 00154 int j = 0; 00155 00156 while (true) { 00157 // Ran out of space 00158 if (j + 1 >= _buffer_size - offset) { 00159 return false; 00160 } 00161 // Receive next character 00162 int c = getc(); 00163 if (c < 0) { 00164 return -1; 00165 } 00166 _buffer[offset + j++] = c; 00167 _buffer[offset + j] = 0; 00168 00169 // Check for match 00170 int count = -1; 00171 sscanf(_buffer + offset, _buffer, &count); 00172 00173 // We only succeed if all characters in the response are matched 00174 if (count == j) { 00175 // Store the found results 00176 vsscanf(_buffer + offset, format, args); 00177 return j; 00178 } 00179 } 00180 } 00181 00182 00183 // Command parsing with line handling 00184 bool ATCmdParser::vsend(const char *command, va_list args) 00185 { 00186 // Create and send command 00187 if (vsprintf(_buffer, command, args) < 0) { 00188 return false; 00189 } 00190 00191 for (int i = 0; _buffer[i]; i++) { 00192 if (putc(_buffer[i]) < 0) { 00193 return false; 00194 } 00195 } 00196 00197 // Finish with newline 00198 for (size_t i = 0; _output_delimiter[i]; i++) { 00199 if (putc(_output_delimiter[i]) < 0) { 00200 return false; 00201 } 00202 } 00203 00204 debug_if(_dbg_on, "AT> %s\n", _buffer); 00205 return true; 00206 } 00207 00208 bool ATCmdParser::vrecv(const char *response, va_list args) 00209 { 00210 restart: 00211 _aborted = false; 00212 // Iterate through each line in the expected response 00213 // response being NULL means we just want to check for OOBs 00214 while (!response || response[0]) { 00215 // Since response is const, we need to copy it into our buffer to 00216 // add the line's null terminator and clobber value-matches with asterisks. 00217 // 00218 // We just use the beginning of the buffer to avoid unnecessary allocations. 00219 int i = 0; 00220 int offset = 0; 00221 bool whole_line_wanted = false; 00222 00223 while (response && response[i]) { 00224 if (response[i] == '%' && response[i + 1] != '%' && response[i + 1] != '*') { 00225 _buffer[offset++] = '%'; 00226 _buffer[offset++] = '*'; 00227 i++; 00228 } else { 00229 _buffer[offset++] = response[i++]; 00230 // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification 00231 if (response[i - 1] == '\n' && !(i >= 3 && response[i - 3] == '[' && response[i - 2] == '^')) { 00232 whole_line_wanted = true; 00233 break; 00234 } 00235 } 00236 } 00237 00238 // Scanf has very poor support for catching errors 00239 // fortunately, we can abuse the %n specifier to determine 00240 // if the entire string was matched. 00241 _buffer[offset++] = '%'; 00242 _buffer[offset++] = 'n'; 00243 _buffer[offset++] = 0; 00244 00245 debug_if(_dbg_on, "AT? %s\n", _buffer); 00246 // To workaround scanf's lack of error reporting, we actually 00247 // make two passes. One checks the validity with the modified 00248 // format string that only stores the matched characters (%n). 00249 // The other reads in the actual matched values. 00250 // 00251 // We keep trying the match until we succeed or some other error 00252 // derails us. 00253 int j = 0; 00254 00255 while (true) { 00256 // If just peeking for OOBs, and at start of line, check 00257 // readability 00258 if (!response && j == 0 && !_fh->readable()) { 00259 return false; 00260 } 00261 // Receive next character 00262 int c = getc(); 00263 if (c < 0) { 00264 debug_if(_dbg_on, "AT(Timeout)\n"); 00265 return false; 00266 } 00267 // Simplify newlines (borrowed from retarget.cpp) 00268 if ((c == CR && _in_prev != LF) || 00269 (c == LF && _in_prev != CR)) { 00270 _in_prev = c; 00271 c = '\n'; 00272 } else if ((c == CR && _in_prev == LF) || 00273 (c == LF && _in_prev == CR)) { 00274 _in_prev = c; 00275 // onto next character 00276 continue; 00277 } else { 00278 _in_prev = c; 00279 } 00280 _buffer[offset + j++] = c; 00281 _buffer[offset + j] = 0; 00282 00283 // Check for oob data 00284 for (struct oob *oob = _oobs; oob; oob = oob->next) { 00285 if ((unsigned)j == oob->len && memcmp( 00286 oob->prefix, _buffer + offset, oob->len) == 0) { 00287 debug_if(_dbg_on, "AT! %s\n", oob->prefix); 00288 _oob_cb_count++; 00289 oob->cb(); 00290 00291 if (_aborted) { 00292 debug_if(_dbg_on, "AT(Aborted)\n"); 00293 return false; 00294 } 00295 // oob may have corrupted non-reentrant buffer, 00296 // so we need to set it up again 00297 goto restart; 00298 } 00299 } 00300 00301 // Check for match 00302 int count = -1; 00303 if (whole_line_wanted && c != '\n') { 00304 // Don't attempt scanning until we get delimiter if they included it in format 00305 // This allows recv("Foo: %s\n") to work, and not match with just the first character of a string 00306 // (scanf does not itself match whitespace in its format string, so \n is not significant to it) 00307 } else if (response) { 00308 sscanf(_buffer + offset, _buffer, &count); 00309 } 00310 00311 // We only succeed if all characters in the response are matched 00312 if (count == j) { 00313 debug_if(_dbg_on, "AT= %s\n", _buffer + offset); 00314 // Reuse the front end of the buffer 00315 memcpy(_buffer, response, i); 00316 _buffer[i] = 0; 00317 00318 // Store the found results 00319 vsscanf(_buffer + offset, _buffer, args); 00320 00321 // Jump to next line and continue parsing 00322 response += i; 00323 break; 00324 } 00325 00326 // Clear the buffer when we hit a newline or ran out of space 00327 // running out of space usually means we ran into binary data 00328 if (c == '\n' || j + 1 >= _buffer_size - offset) { 00329 debug_if(_dbg_on, "AT< %s", _buffer + offset); 00330 j = 0; 00331 } 00332 } 00333 } 00334 00335 return true; 00336 } 00337 00338 // Mapping to vararg functions 00339 int ATCmdParser::printf(const char *format, ...) 00340 { 00341 va_list args; 00342 va_start(args, format); 00343 int res = vprintf(format, args); 00344 va_end(args); 00345 return res; 00346 } 00347 00348 int ATCmdParser::scanf(const char *format, ...) 00349 { 00350 va_list args; 00351 va_start(args, format); 00352 int res = vscanf(format, args); 00353 va_end(args); 00354 return res; 00355 } 00356 00357 bool ATCmdParser::send(const char *command, ...) 00358 { 00359 va_list args; 00360 va_start(args, command); 00361 bool res = vsend(command, args); 00362 va_end(args); 00363 return res; 00364 } 00365 00366 bool ATCmdParser::recv(const char *response, ...) 00367 { 00368 va_list args; 00369 va_start(args, response); 00370 bool res = vrecv(response, args); 00371 va_end(args); 00372 return res; 00373 } 00374 00375 // oob registration 00376 void ATCmdParser::oob(const char *prefix, Callback<void()> cb) 00377 { 00378 struct oob *oob = new struct oob; 00379 oob->len = strlen(prefix); 00380 oob->prefix = prefix; 00381 oob->cb = cb; 00382 oob->next = _oobs; 00383 _oobs = oob; 00384 } 00385 00386 void ATCmdParser::abort() 00387 { 00388 _aborted = true; 00389 } 00390 00391 bool ATCmdParser::process_oob() 00392 { 00393 int pre_count = _oob_cb_count; 00394 recv(NULL); 00395 return _oob_cb_count != pre_count; 00396 } 00397 00398
Generated on Tue Jul 12 2022 15:15:40 by
