Parser for AT commands and similar protocols

Dependencies:   BufferedSerial

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

Fork of ATParser by NetworkSocketAPI

Committer:
geky
Date:
Mon Jul 20 21:28:39 2015 +0000
Revision:
9:9bcb87c27208
Parent:
8:91515b168c70
Child:
10:553f9ffaf657
Added direct printf/scanf methods as alternatives to the newline-based send/recv methods.

Who changed what in which revision?

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