Parser for AT commands and similar protocols

Dependencies:   BufferedSerial

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