Parser for AT commands and similar protocols

Dependencies:   BufferedSerial

Committer:
sam_grove
Date:
Sun Jul 26 21:53:28 2015 +0000
Revision:
10:553f9ffaf657
Parent:
9:9bcb87c27208
Child:
11:fd406d4c4227
Small changes

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