Parser for AT commands and similar protocols
ATParser.cpp@7:d1b193880af1, 2015-07-20 (annotated)
- 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?
User | Revision | Line number | New 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 | } |