Parser for AT commands and similar protocols

Dependencies:   BufferedSerial

Committer:
geky
Date:
Fri Jul 17 17:23:57 2015 +0000
Revision:
5:26bc9255b751
Parent:
4:38acbd6f9d9e
Child:
6:51f1171b5ebc
Seperated command into send/recv components for seperate use

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