Parser for AT commands and similar protocols

Dependencies:   BufferedSerial

Committer:
Christopher Haster
Date:
Thu Feb 18 15:59:15 2016 -0600
Revision:
12:7d3c3f7ce928
Parent:
11:fd406d4c4227
Child:
13:46a18ad08efc
Removed carriage returns

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Christopher Haster 12:7d3c3f7ce928 1 /* Copyright (c) 2015 ARM Limited
Christopher Haster 12:7d3c3f7ce928 2 *
Christopher Haster 12:7d3c3f7ce928 3 * Licensed under the Apache License, Version 2.0 (the "License");
Christopher Haster 12:7d3c3f7ce928 4 * you may not use this file except in compliance with the License.
Christopher Haster 12:7d3c3f7ce928 5 * You may obtain a copy of the License at
Christopher Haster 12:7d3c3f7ce928 6 *
Christopher Haster 12:7d3c3f7ce928 7 * http://www.apache.org/licenses/LICENSE-2.0
Christopher Haster 12:7d3c3f7ce928 8 *
Christopher Haster 12:7d3c3f7ce928 9 * Unless required by applicable law or agreed to in writing, software
Christopher Haster 12:7d3c3f7ce928 10 * distributed under the License is distributed on an "AS IS" BASIS,
Christopher Haster 12:7d3c3f7ce928 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Christopher Haster 12:7d3c3f7ce928 12 * See the License for the specific language governing permissions and
Christopher Haster 12:7d3c3f7ce928 13 * limitations under the License.
Christopher Haster 12:7d3c3f7ce928 14 *
Christopher Haster 12:7d3c3f7ce928 15 * @section DESCRIPTION
Christopher Haster 12:7d3c3f7ce928 16 *
Christopher Haster 12:7d3c3f7ce928 17 * Parser for the AT command syntax
Christopher Haster 12:7d3c3f7ce928 18 *
Christopher Haster 12:7d3c3f7ce928 19 */
Christopher Haster 12:7d3c3f7ce928 20
Christopher Haster 12:7d3c3f7ce928 21 #include "ATParser.h"
Christopher Haster 12:7d3c3f7ce928 22 #include "mbed_debug.h"
Christopher Haster 12:7d3c3f7ce928 23
Christopher Haster 12:7d3c3f7ce928 24
Christopher Haster 12:7d3c3f7ce928 25 // getc/putc handling with timeouts
Christopher Haster 12:7d3c3f7ce928 26 int ATParser::putc(char c)
Christopher Haster 12:7d3c3f7ce928 27 {
Christopher Haster 12:7d3c3f7ce928 28 Timer timer;
Christopher Haster 12:7d3c3f7ce928 29 timer.start();
Christopher Haster 12:7d3c3f7ce928 30
Christopher Haster 12:7d3c3f7ce928 31 while (true) {
Christopher Haster 12:7d3c3f7ce928 32 if (_serial->writeable()) {
Christopher Haster 12:7d3c3f7ce928 33 return _serial->putc(c);
Christopher Haster 12:7d3c3f7ce928 34 }
Christopher Haster 12:7d3c3f7ce928 35 if (timer.read_ms() > _timeout) {
Christopher Haster 12:7d3c3f7ce928 36 return -1;
Christopher Haster 12:7d3c3f7ce928 37 }
Christopher Haster 12:7d3c3f7ce928 38 }
Christopher Haster 12:7d3c3f7ce928 39 }
Christopher Haster 12:7d3c3f7ce928 40
Christopher Haster 12:7d3c3f7ce928 41 int ATParser::getc()
Christopher Haster 12:7d3c3f7ce928 42 {
Christopher Haster 12:7d3c3f7ce928 43 Timer timer;
Christopher Haster 12:7d3c3f7ce928 44 timer.start();
Christopher Haster 12:7d3c3f7ce928 45
Christopher Haster 12:7d3c3f7ce928 46 while (true) {
Christopher Haster 12:7d3c3f7ce928 47 if (_serial->readable()) {
Christopher Haster 12:7d3c3f7ce928 48 return _serial->getc();
Christopher Haster 12:7d3c3f7ce928 49 }
Christopher Haster 12:7d3c3f7ce928 50 if (timer.read_ms() > _timeout) {
Christopher Haster 12:7d3c3f7ce928 51 return -1;
Christopher Haster 12:7d3c3f7ce928 52 }
Christopher Haster 12:7d3c3f7ce928 53 }
Christopher Haster 12:7d3c3f7ce928 54 }
Christopher Haster 12:7d3c3f7ce928 55
Christopher Haster 12:7d3c3f7ce928 56 void ATParser::flush()
Christopher Haster 12:7d3c3f7ce928 57 {
Christopher Haster 12:7d3c3f7ce928 58 while (_serial->readable()) {
Christopher Haster 12:7d3c3f7ce928 59 _serial->getc();
Christopher Haster 12:7d3c3f7ce928 60 }
Christopher Haster 12:7d3c3f7ce928 61 }
Christopher Haster 12:7d3c3f7ce928 62
Christopher Haster 12:7d3c3f7ce928 63
Christopher Haster 12:7d3c3f7ce928 64 // read/write handling with timeouts
Christopher Haster 12:7d3c3f7ce928 65 int ATParser::write(const char *data, int size)
Christopher Haster 12:7d3c3f7ce928 66 {
Christopher Haster 12:7d3c3f7ce928 67 int i = 0;
Christopher Haster 12:7d3c3f7ce928 68 for ( ; i < size; i++) {
Christopher Haster 12:7d3c3f7ce928 69 if (putc(data[i]) < 0) {
Christopher Haster 12:7d3c3f7ce928 70 return -1;
Christopher Haster 12:7d3c3f7ce928 71 }
Christopher Haster 12:7d3c3f7ce928 72 }
Christopher Haster 12:7d3c3f7ce928 73 return i;
Christopher Haster 12:7d3c3f7ce928 74 }
Christopher Haster 12:7d3c3f7ce928 75
Christopher Haster 12:7d3c3f7ce928 76 int ATParser::read(char *data, int size)
Christopher Haster 12:7d3c3f7ce928 77 {
Christopher Haster 12:7d3c3f7ce928 78 int i = 0;
Christopher Haster 12:7d3c3f7ce928 79 for ( ; i < size; i++) {
Christopher Haster 12:7d3c3f7ce928 80 int c = getc();
Christopher Haster 12:7d3c3f7ce928 81 if (c < 0) {
Christopher Haster 12:7d3c3f7ce928 82 return -1;
Christopher Haster 12:7d3c3f7ce928 83 }
Christopher Haster 12:7d3c3f7ce928 84 data[i] = c;
Christopher Haster 12:7d3c3f7ce928 85 }
Christopher Haster 12:7d3c3f7ce928 86 return i;
Christopher Haster 12:7d3c3f7ce928 87 }
Christopher Haster 12:7d3c3f7ce928 88
Christopher Haster 12:7d3c3f7ce928 89
Christopher Haster 12:7d3c3f7ce928 90 // printf/scanf handling
Christopher Haster 12:7d3c3f7ce928 91 int ATParser::vprintf(const char *format, va_list args)
Christopher Haster 12:7d3c3f7ce928 92 {
Christopher Haster 12:7d3c3f7ce928 93 if (vsprintf(_buffer, format, args) < 0) {
Christopher Haster 12:7d3c3f7ce928 94 return false;
Christopher Haster 12:7d3c3f7ce928 95 }
Christopher Haster 12:7d3c3f7ce928 96 int i = 0;
Christopher Haster 12:7d3c3f7ce928 97 for ( ; _buffer[i]; i++) {
Christopher Haster 12:7d3c3f7ce928 98 if (putc(_buffer[i]) < 0) {
Christopher Haster 12:7d3c3f7ce928 99 return -1;
Christopher Haster 12:7d3c3f7ce928 100 }
Christopher Haster 12:7d3c3f7ce928 101 }
Christopher Haster 12:7d3c3f7ce928 102 return i;
Christopher Haster 12:7d3c3f7ce928 103 }
Christopher Haster 12:7d3c3f7ce928 104
Christopher Haster 12:7d3c3f7ce928 105 int ATParser::vscanf(const char *format, va_list args)
Christopher Haster 12:7d3c3f7ce928 106 {
Christopher Haster 12:7d3c3f7ce928 107 // Since format is const, we need to copy it into our buffer to
Christopher Haster 12:7d3c3f7ce928 108 // add the line's null terminator and clobber value-matches with asterisks.
Christopher Haster 12:7d3c3f7ce928 109 //
Christopher Haster 12:7d3c3f7ce928 110 // We just use the beginning of the buffer to avoid unnecessary allocations.
Christopher Haster 12:7d3c3f7ce928 111 int i = 0;
Christopher Haster 12:7d3c3f7ce928 112 int offset = 0;
Christopher Haster 12:7d3c3f7ce928 113
Christopher Haster 12:7d3c3f7ce928 114 while (format[i]) {
Christopher Haster 12:7d3c3f7ce928 115 if (format[i] == '%' && format[i+1] != '%' && format[i+1] != '*') {
Christopher Haster 12:7d3c3f7ce928 116 _buffer[offset++] = '%';
Christopher Haster 12:7d3c3f7ce928 117 _buffer[offset++] = '*';
Christopher Haster 12:7d3c3f7ce928 118 i++;
Christopher Haster 12:7d3c3f7ce928 119 } else {
Christopher Haster 12:7d3c3f7ce928 120 _buffer[offset++] = format[i++];
Christopher Haster 12:7d3c3f7ce928 121 }
Christopher Haster 12:7d3c3f7ce928 122 }
Christopher Haster 12:7d3c3f7ce928 123
Christopher Haster 12:7d3c3f7ce928 124 // Scanf has very poor support for catching errors
Christopher Haster 12:7d3c3f7ce928 125 // fortunately, we can abuse the %n specifier to determine
Christopher Haster 12:7d3c3f7ce928 126 // if the entire string was matched.
Christopher Haster 12:7d3c3f7ce928 127 _buffer[offset++] = '%';
Christopher Haster 12:7d3c3f7ce928 128 _buffer[offset++] = 'n';
Christopher Haster 12:7d3c3f7ce928 129 _buffer[offset++] = 0;
Christopher Haster 12:7d3c3f7ce928 130
Christopher Haster 12:7d3c3f7ce928 131 // To workaround scanf's lack of error reporting, we actually
Christopher Haster 12:7d3c3f7ce928 132 // make two passes. One checks the validity with the modified
Christopher Haster 12:7d3c3f7ce928 133 // format string that only stores the matched characters (%n).
Christopher Haster 12:7d3c3f7ce928 134 // The other reads in the actual matched values.
Christopher Haster 12:7d3c3f7ce928 135 //
Christopher Haster 12:7d3c3f7ce928 136 // We keep trying the match until we succeed or some other error
Christopher Haster 12:7d3c3f7ce928 137 // derails us.
Christopher Haster 12:7d3c3f7ce928 138 int j = 0;
Christopher Haster 12:7d3c3f7ce928 139
Christopher Haster 12:7d3c3f7ce928 140 while (true) {
Christopher Haster 12:7d3c3f7ce928 141 // Ran out of space
Christopher Haster 12:7d3c3f7ce928 142 if (j+1 >= _buffer_size - offset) {
Christopher Haster 12:7d3c3f7ce928 143 return false;
Christopher Haster 12:7d3c3f7ce928 144 }
Christopher Haster 12:7d3c3f7ce928 145 // Recieve next character
Christopher Haster 12:7d3c3f7ce928 146 int c = getc();
Christopher Haster 12:7d3c3f7ce928 147 if (c < 0) {
Christopher Haster 12:7d3c3f7ce928 148 return -1;
Christopher Haster 12:7d3c3f7ce928 149 }
Christopher Haster 12:7d3c3f7ce928 150 _buffer[offset + j++] = c;
Christopher Haster 12:7d3c3f7ce928 151 _buffer[offset + j] = 0;
Christopher Haster 12:7d3c3f7ce928 152
Christopher Haster 12:7d3c3f7ce928 153 // Check for match
Christopher Haster 12:7d3c3f7ce928 154 int count = -1;
Christopher Haster 12:7d3c3f7ce928 155 sscanf(_buffer+offset, _buffer, &count);
Christopher Haster 12:7d3c3f7ce928 156
Christopher Haster 12:7d3c3f7ce928 157 // We only succeed if all characters in the response are matched
Christopher Haster 12:7d3c3f7ce928 158 if (count == j) {
Christopher Haster 12:7d3c3f7ce928 159 // Store the found results
Christopher Haster 12:7d3c3f7ce928 160 vsscanf(_buffer+offset, format, args);
Christopher Haster 12:7d3c3f7ce928 161 return j;
Christopher Haster 12:7d3c3f7ce928 162 }
Christopher Haster 12:7d3c3f7ce928 163 }
Christopher Haster 12:7d3c3f7ce928 164 }
Christopher Haster 12:7d3c3f7ce928 165
Christopher Haster 12:7d3c3f7ce928 166
Christopher Haster 12:7d3c3f7ce928 167 // Command parsing with line handling
Christopher Haster 12:7d3c3f7ce928 168 bool ATParser::vsend(const char *command, va_list args)
Christopher Haster 12:7d3c3f7ce928 169 {
Christopher Haster 12:7d3c3f7ce928 170 // Create and send command
Christopher Haster 12:7d3c3f7ce928 171 if (vsprintf(_buffer, command, args) < 0) {
Christopher Haster 12:7d3c3f7ce928 172 return false;
Christopher Haster 12:7d3c3f7ce928 173 }
Christopher Haster 12:7d3c3f7ce928 174 for (int i = 0; _buffer[i]; i++) {
Christopher Haster 12:7d3c3f7ce928 175 if (putc(_buffer[i]) < 0) {
Christopher Haster 12:7d3c3f7ce928 176 return false;
Christopher Haster 12:7d3c3f7ce928 177 }
Christopher Haster 12:7d3c3f7ce928 178 }
Christopher Haster 12:7d3c3f7ce928 179
Christopher Haster 12:7d3c3f7ce928 180 // Finish with newline
Christopher Haster 12:7d3c3f7ce928 181 for (int i = 0; _delimiter[i]; i++) {
Christopher Haster 12:7d3c3f7ce928 182 if (putc(_delimiter[i]) < 0) {
Christopher Haster 12:7d3c3f7ce928 183 return false;
Christopher Haster 12:7d3c3f7ce928 184 }
Christopher Haster 12:7d3c3f7ce928 185 }
Christopher Haster 12:7d3c3f7ce928 186
Christopher Haster 12:7d3c3f7ce928 187 debug_if(dbg_on, "AT> %s\r\n", _buffer);
Christopher Haster 12:7d3c3f7ce928 188 return true;
Christopher Haster 12:7d3c3f7ce928 189 }
Christopher Haster 12:7d3c3f7ce928 190
Christopher Haster 12:7d3c3f7ce928 191 bool ATParser::vrecv(const char *response, va_list args)
Christopher Haster 12:7d3c3f7ce928 192 {
Christopher Haster 12:7d3c3f7ce928 193 // Iterate through each line in the expected response
Christopher Haster 12:7d3c3f7ce928 194 while (response[0]) {
Christopher Haster 12:7d3c3f7ce928 195 // Since response is const, we need to copy it into our buffer to
Christopher Haster 12:7d3c3f7ce928 196 // add the line's null terminator and clobber value-matches with asterisks.
Christopher Haster 12:7d3c3f7ce928 197 //
Christopher Haster 12:7d3c3f7ce928 198 // We just use the beginning of the buffer to avoid unnecessary allocations.
Christopher Haster 12:7d3c3f7ce928 199 int i = 0;
Christopher Haster 12:7d3c3f7ce928 200 int offset = 0;
Christopher Haster 12:7d3c3f7ce928 201
Christopher Haster 12:7d3c3f7ce928 202 while (response[i]) {
Christopher Haster 12:7d3c3f7ce928 203 if (memcmp(&response[i+1-_delim_size], _delimiter, _delim_size) == 0) {
Christopher Haster 12:7d3c3f7ce928 204 i++;
Christopher Haster 12:7d3c3f7ce928 205 break;
Christopher Haster 12:7d3c3f7ce928 206 } else if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') {
Christopher Haster 12:7d3c3f7ce928 207 _buffer[offset++] = '%';
Christopher Haster 12:7d3c3f7ce928 208 _buffer[offset++] = '*';
Christopher Haster 12:7d3c3f7ce928 209 i++;
Christopher Haster 12:7d3c3f7ce928 210 } else {
Christopher Haster 12:7d3c3f7ce928 211 _buffer[offset++] = response[i++];
Christopher Haster 12:7d3c3f7ce928 212 }
Christopher Haster 12:7d3c3f7ce928 213 }
Christopher Haster 12:7d3c3f7ce928 214
Christopher Haster 12:7d3c3f7ce928 215 // Scanf has very poor support for catching errors
Christopher Haster 12:7d3c3f7ce928 216 // fortunately, we can abuse the %n specifier to determine
Christopher Haster 12:7d3c3f7ce928 217 // if the entire string was matched.
Christopher Haster 12:7d3c3f7ce928 218 _buffer[offset++] = '%';
Christopher Haster 12:7d3c3f7ce928 219 _buffer[offset++] = 'n';
Christopher Haster 12:7d3c3f7ce928 220 _buffer[offset++] = 0;
Christopher Haster 12:7d3c3f7ce928 221
Christopher Haster 12:7d3c3f7ce928 222 // To workaround scanf's lack of error reporting, we actually
Christopher Haster 12:7d3c3f7ce928 223 // make two passes. One checks the validity with the modified
Christopher Haster 12:7d3c3f7ce928 224 // format string that only stores the matched characters (%n).
Christopher Haster 12:7d3c3f7ce928 225 // The other reads in the actual matched values.
Christopher Haster 12:7d3c3f7ce928 226 //
Christopher Haster 12:7d3c3f7ce928 227 // We keep trying the match until we succeed or some other error
Christopher Haster 12:7d3c3f7ce928 228 // derails us.
Christopher Haster 12:7d3c3f7ce928 229 int j = 0;
Christopher Haster 12:7d3c3f7ce928 230
Christopher Haster 12:7d3c3f7ce928 231 while (true) {
Christopher Haster 12:7d3c3f7ce928 232 // Ran out of space
Christopher Haster 12:7d3c3f7ce928 233 if (j+1 >= _buffer_size - offset) {
Christopher Haster 12:7d3c3f7ce928 234 return false;
Christopher Haster 12:7d3c3f7ce928 235 }
Christopher Haster 12:7d3c3f7ce928 236 // Recieve next character
Christopher Haster 12:7d3c3f7ce928 237 int c = getc();
Christopher Haster 12:7d3c3f7ce928 238 if (c < 0) {
Christopher Haster 12:7d3c3f7ce928 239 return false;
Christopher Haster 12:7d3c3f7ce928 240 }
Christopher Haster 12:7d3c3f7ce928 241 _buffer[offset + j++] = c;
Christopher Haster 12:7d3c3f7ce928 242 _buffer[offset + j] = 0;
Christopher Haster 12:7d3c3f7ce928 243
Christopher Haster 12:7d3c3f7ce928 244 // Check for match
Christopher Haster 12:7d3c3f7ce928 245 int count = -1;
Christopher Haster 12:7d3c3f7ce928 246 sscanf(_buffer+offset, _buffer, &count);
Christopher Haster 12:7d3c3f7ce928 247
Christopher Haster 12:7d3c3f7ce928 248 // We only succeed if all characters in the response are matched
Christopher Haster 12:7d3c3f7ce928 249 if (count == j) {
Christopher Haster 12:7d3c3f7ce928 250 debug_if(dbg_on, "AT= %s\r\n", _buffer+offset);
Christopher Haster 12:7d3c3f7ce928 251 // Reuse the front end of the buffer
Christopher Haster 12:7d3c3f7ce928 252 memcpy(_buffer, response, i);
Christopher Haster 12:7d3c3f7ce928 253 _buffer[i] = 0;
Christopher Haster 12:7d3c3f7ce928 254
Christopher Haster 12:7d3c3f7ce928 255 // Store the found results
Christopher Haster 12:7d3c3f7ce928 256 vsscanf(_buffer+offset, _buffer, args);
Christopher Haster 12:7d3c3f7ce928 257
Christopher Haster 12:7d3c3f7ce928 258 // Jump to next line and continue parsing
Christopher Haster 12:7d3c3f7ce928 259 response += i;
Christopher Haster 12:7d3c3f7ce928 260 break;
Christopher Haster 12:7d3c3f7ce928 261 }
Christopher Haster 12:7d3c3f7ce928 262
Christopher Haster 12:7d3c3f7ce928 263 // Clear the buffer when we hit a newline
Christopher Haster 12:7d3c3f7ce928 264 if (strcmp(&_buffer[offset + j-_delim_size], _delimiter) == 0) {
Christopher Haster 12:7d3c3f7ce928 265 debug_if(dbg_on, "AT< %s", _buffer+offset);
Christopher Haster 12:7d3c3f7ce928 266 j = 0;
Christopher Haster 12:7d3c3f7ce928 267 }
Christopher Haster 12:7d3c3f7ce928 268 }
Christopher Haster 12:7d3c3f7ce928 269 }
Christopher Haster 12:7d3c3f7ce928 270
Christopher Haster 12:7d3c3f7ce928 271 return true;
Christopher Haster 12:7d3c3f7ce928 272 }
Christopher Haster 12:7d3c3f7ce928 273
Christopher Haster 12:7d3c3f7ce928 274
Christopher Haster 12:7d3c3f7ce928 275 // Mapping to vararg functions
Christopher Haster 12:7d3c3f7ce928 276 int ATParser::printf(const char *format, ...)
Christopher Haster 12:7d3c3f7ce928 277 {
Christopher Haster 12:7d3c3f7ce928 278 va_list args;
Christopher Haster 12:7d3c3f7ce928 279 va_start(args, format);
Christopher Haster 12:7d3c3f7ce928 280 int res = vprintf(format, args);
Christopher Haster 12:7d3c3f7ce928 281 va_end(args);
Christopher Haster 12:7d3c3f7ce928 282 return res;
Christopher Haster 12:7d3c3f7ce928 283 }
Christopher Haster 12:7d3c3f7ce928 284
Christopher Haster 12:7d3c3f7ce928 285 int ATParser::scanf(const char *format, ...)
Christopher Haster 12:7d3c3f7ce928 286 {
Christopher Haster 12:7d3c3f7ce928 287 va_list args;
Christopher Haster 12:7d3c3f7ce928 288 va_start(args, format);
Christopher Haster 12:7d3c3f7ce928 289 int res = vscanf(format, args);
Christopher Haster 12:7d3c3f7ce928 290 va_end(args);
Christopher Haster 12:7d3c3f7ce928 291 return res;
Christopher Haster 12:7d3c3f7ce928 292 }
Christopher Haster 12:7d3c3f7ce928 293
Christopher Haster 12:7d3c3f7ce928 294 bool ATParser::send(const char *command, ...)
Christopher Haster 12:7d3c3f7ce928 295 {
Christopher Haster 12:7d3c3f7ce928 296 va_list args;
Christopher Haster 12:7d3c3f7ce928 297 va_start(args, command);
Christopher Haster 12:7d3c3f7ce928 298 bool res = vsend(command, args);
Christopher Haster 12:7d3c3f7ce928 299 va_end(args);
Christopher Haster 12:7d3c3f7ce928 300 return res;
Christopher Haster 12:7d3c3f7ce928 301 }
Christopher Haster 12:7d3c3f7ce928 302
Christopher Haster 12:7d3c3f7ce928 303 bool ATParser::recv(const char *response, ...)
Christopher Haster 12:7d3c3f7ce928 304 {
Christopher Haster 12:7d3c3f7ce928 305 va_list args;
Christopher Haster 12:7d3c3f7ce928 306 va_start(args, response);
Christopher Haster 12:7d3c3f7ce928 307 bool res = vrecv(response, args);
Christopher Haster 12:7d3c3f7ce928 308 va_end(args);
Christopher Haster 12:7d3c3f7ce928 309 return res;
Christopher Haster 12:7d3c3f7ce928 310 }