Parser for AT commands and similar protocols

Dependencies:   BufferedSerial

Committer:
geky
Date:
Mon Jul 20 18:23:54 2015 +0000
Revision:
7:d1b193880af1
Parent:
6:51f1171b5ebc
Child:
8:91515b168c70
Modified response parsing to try every character instead of waiting for newlines. This means the ATParser can now handle trailing binary data in AT commands.

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
geky 6:51f1171b5ebc 64 for (i = 0; i < size; i++) {
geky 6:51f1171b5ebc 65 if (putc(data[i]) < 0)
geky 6:51f1171b5ebc 66 return i;
geky 6:51f1171b5ebc 67 }
geky 6:51f1171b5ebc 68
geky 6:51f1171b5ebc 69 return i;
geky 6:51f1171b5ebc 70 }
geky 6:51f1171b5ebc 71
geky 6:51f1171b5ebc 72 int ATParser::read(char *data, int size) {
geky 6:51f1171b5ebc 73 int i;
geky 6:51f1171b5ebc 74
geky 6:51f1171b5ebc 75 for (i = 0; i < size; i++) {
geky 6:51f1171b5ebc 76 int c = getc();
geky 6:51f1171b5ebc 77
geky 6:51f1171b5ebc 78 if (c < 0)
geky 6:51f1171b5ebc 79 return i;
geky 6:51f1171b5ebc 80
geky 6:51f1171b5ebc 81 data[i] = c;
geky 6:51f1171b5ebc 82 }
geky 6:51f1171b5ebc 83
geky 6:51f1171b5ebc 84 return i;
geky 6:51f1171b5ebc 85 }
geky 6:51f1171b5ebc 86
geky 1:66a14afe650a 87
geky 7:d1b193880af1 88 // Command parsing with line handling
geky 7:d1b193880af1 89 bool ATParser::vsend(const char *command, va_list args) {
geky 7:d1b193880af1 90 flush();
geky 7:d1b193880af1 91
geky 7:d1b193880af1 92 // Create and send command
geky 7:d1b193880af1 93 if (vsprintf(_buffer, command, args) < 0)
geky 7:d1b193880af1 94 return false;
geky 7:d1b193880af1 95
geky 7:d1b193880af1 96 for (int i = 0; _buffer[i]; i++) {
geky 7:d1b193880af1 97 if (putc(_buffer[i]) < 0)
geky 0:c741e144517c 98 return false;
geky 0:c741e144517c 99 }
geky 0:c741e144517c 100
geky 0:c741e144517c 101 // Finish with newline
geky 3:32915b9467d2 102 for (int i = 0; _delimiter[i]; i++) {
geky 4:38acbd6f9d9e 103 if (putc(_delimiter[i]) < 0)
geky 3:32915b9467d2 104 return false;
geky 3:32915b9467d2 105 }
geky 0:c741e144517c 106
geky 0:c741e144517c 107 #ifdef AT_ECHO
geky 7:d1b193880af1 108 printf("AT> %s\r\n", _buffer);
geky 0:c741e144517c 109 #endif
geky 5:26bc9255b751 110
geky 5:26bc9255b751 111 return true;
geky 5:26bc9255b751 112 }
geky 5:26bc9255b751 113
geky 5:26bc9255b751 114 bool ATParser::vrecv(const char *response, va_list args) {
geky 1:66a14afe650a 115 // Iterate through each line in the expected response
geky 5:26bc9255b751 116 while (response[0]) {
geky 1:66a14afe650a 117 // Since response is const, we need to copy it into our buffer to
geky 7:d1b193880af1 118 // add the line's null terminator and clobber value-matches with asterisks.
geky 1:66a14afe650a 119 //
geky 7:d1b193880af1 120 // We just use the beginning of the buffer to avoid unnecessary allocations.
geky 1:66a14afe650a 121 int i = 0;
geky 1:66a14afe650a 122 int offset = 0;
geky 1:66a14afe650a 123
geky 1:66a14afe650a 124 while (response[i]) {
geky 5:26bc9255b751 125 if (memcmp(&response[i+1-_delim_size], _delimiter, _delim_size) == 0) {
geky 5:26bc9255b751 126 i++;
geky 1:66a14afe650a 127 break;
geky 1:66a14afe650a 128 } else if (response[i] == '%' &&
geky 1:66a14afe650a 129 response[i+1] != '%' &&
geky 1:66a14afe650a 130 response[i+1] != '*') {
geky 1:66a14afe650a 131 _buffer[offset++] = '%';
geky 1:66a14afe650a 132 _buffer[offset++] = '*';
geky 3:32915b9467d2 133 i++;
geky 1:66a14afe650a 134 } else {
geky 1:66a14afe650a 135 _buffer[offset++] = response[i++];
geky 1:66a14afe650a 136 }
geky 1:66a14afe650a 137 }
geky 1:66a14afe650a 138
geky 1:66a14afe650a 139 // Scanf has very poor support for catching errors
geky 1:66a14afe650a 140 // fortunately, we can abuse the %n specifier to determine
geky 1:66a14afe650a 141 // if the entire string was matched.
geky 1:66a14afe650a 142 _buffer[offset++] = '%';
geky 1:66a14afe650a 143 _buffer[offset++] = 'n';
geky 1:66a14afe650a 144 _buffer[offset++] = 0;
geky 1:66a14afe650a 145
geky 1:66a14afe650a 146 // To workaround scanf's lack of error reporting, we actually
geky 1:66a14afe650a 147 // make two passes. One checks the validity with the modified
geky 1:66a14afe650a 148 // format string that only stores the matched characters (%n).
geky 1:66a14afe650a 149 // The other reads in the actual matched values.
geky 1:66a14afe650a 150 //
geky 1:66a14afe650a 151 // We keep trying the match until we succeed or some other error
geky 1:66a14afe650a 152 // derails us.
geky 7:d1b193880af1 153 int j = 0;
geky 7:d1b193880af1 154
geky 7:d1b193880af1 155 while (j+1 < _buffer_size - offset) {
geky 7:d1b193880af1 156 // Recieve next character
geky 7:d1b193880af1 157 int c = getc();
geky 7:d1b193880af1 158 if (c < 0)
geky 1:66a14afe650a 159 return false;
geky 7:d1b193880af1 160
geky 7:d1b193880af1 161 _buffer[offset + j++] = c;
geky 7:d1b193880af1 162 _buffer[offset + j] = 0;
geky 7:d1b193880af1 163
geky 7:d1b193880af1 164 // Check for match
geky 2:4d68f546861c 165 int count = -1;
geky 1:66a14afe650a 166 sscanf(_buffer+offset, _buffer, &count);
geky 1:66a14afe650a 167
geky 7:d1b193880af1 168 // We only succeed if all characters in the response are matched
geky 7:d1b193880af1 169 if (count == j) {
geky 7:d1b193880af1 170 #ifdef AT_ECHO
geky 7:d1b193880af1 171 printf("AT= %s\r\n", _buffer+offset);
geky 7:d1b193880af1 172 #endif
geky 1:66a14afe650a 173 // Reuse the front end of the buffer
geky 6:51f1171b5ebc 174 memcpy(_buffer, response, i);
geky 6:51f1171b5ebc 175 _buffer[i] = 0;
geky 1:66a14afe650a 176
geky 1:66a14afe650a 177 // Store the found results
geky 1:66a14afe650a 178 vsscanf(_buffer+offset, _buffer, args);
geky 1:66a14afe650a 179
geky 1:66a14afe650a 180 // Jump to next line and continue parsing
geky 1:66a14afe650a 181 response += i;
geky 1:66a14afe650a 182 break;
geky 1:66a14afe650a 183 }
geky 7:d1b193880af1 184
geky 7:d1b193880af1 185 // Clear the buffer when we hit a newline
geky 7:d1b193880af1 186 if (strcmp(&_buffer[offset + j-_delim_size], _delimiter) == 0) {
geky 7:d1b193880af1 187 #ifdef AT_ECHO
geky 7:d1b193880af1 188 printf("AT< %s", _buffer+offset);
geky 7:d1b193880af1 189 #endif
geky 7:d1b193880af1 190 j = 0;
geky 7:d1b193880af1 191 }
geky 1:66a14afe650a 192 }
geky 0:c741e144517c 193 }
geky 5:26bc9255b751 194
geky 5:26bc9255b751 195 return true;
geky 5:26bc9255b751 196 }
geky 5:26bc9255b751 197
geky 5:26bc9255b751 198 bool ATParser::vcommand(const char *command, const char *response, va_list args) {
geky 5:26bc9255b751 199 if (command) {
geky 5:26bc9255b751 200 if (!vsend(command, args))
geky 5:26bc9255b751 201 return false;
geky 5:26bc9255b751 202 }
geky 5:26bc9255b751 203
geky 5:26bc9255b751 204 if (response) {
geky 5:26bc9255b751 205 if (!vrecv(response, args))
geky 5:26bc9255b751 206 return false;
geky 5:26bc9255b751 207 }
geky 5:26bc9255b751 208
geky 0:c741e144517c 209 return true;
geky 0:c741e144517c 210 }
geky 5:26bc9255b751 211
geky 7:d1b193880af1 212
geky 5:26bc9255b751 213 // Mapping to vararg functions
geky 5:26bc9255b751 214 bool ATParser::send(const char *command, ...) {
geky 5:26bc9255b751 215 va_list args;
geky 5:26bc9255b751 216 va_start(args, command);
geky 5:26bc9255b751 217 bool res = vsend(command, args);
geky 5:26bc9255b751 218 va_end(args);
geky 5:26bc9255b751 219 return res;
geky 5:26bc9255b751 220 }
geky 5:26bc9255b751 221
geky 5:26bc9255b751 222 bool ATParser::recv(const char *response, ...) {
geky 5:26bc9255b751 223 va_list args;
geky 5:26bc9255b751 224 va_start(args, response);
geky 5:26bc9255b751 225 bool res = vrecv(response, args);
geky 5:26bc9255b751 226 va_end(args);
geky 5:26bc9255b751 227 return res;
geky 5:26bc9255b751 228 }
geky 5:26bc9255b751 229
geky 5:26bc9255b751 230 bool ATParser::command(const char *command, const char *response, ...) {
geky 5:26bc9255b751 231 va_list args;
geky 5:26bc9255b751 232 va_start(args, response);
geky 5:26bc9255b751 233 bool res = vcommand(command, response, args);
geky 5:26bc9255b751 234 va_end(args);
geky 5:26bc9255b751 235 return res;
geky 5:26bc9255b751 236 }