cc

Dependencies:   BufferedSerial

Committer:
geky
Date:
Fri Jul 17 16:31:58 2015 +0000
Revision:
3:32915b9467d2
Parent:
2:4d68f546861c
Child:
4:38acbd6f9d9e
Added configurable delimiters; ; This does now require explicit delimiters and no long matches the last character.

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 #include <cstdarg>
geky 0:c741e144517c 23
geky 0:c741e144517c 24 // This can be defined to assist in debugging
geky 2:4d68f546861c 25 #define AT_ECHO 1
geky 0:c741e144517c 26
geky 0:c741e144517c 27
geky 0:c741e144517c 28 // getc/putc handling with timeouts
geky 0:c741e144517c 29 int ATParser::_putc(char c) {
geky 0:c741e144517c 30 Timer timer;
geky 0:c741e144517c 31 timer.start();
geky 0:c741e144517c 32
geky 0:c741e144517c 33 while (true) {
geky 0:c741e144517c 34 if (_serial->writeable())
geky 0:c741e144517c 35 return _serial->putc(c);
geky 0:c741e144517c 36
geky 0:c741e144517c 37 if (timer.read_ms() > _timeout)
geky 0:c741e144517c 38 return -1;
geky 0:c741e144517c 39 }
geky 0:c741e144517c 40 }
geky 0:c741e144517c 41
geky 0:c741e144517c 42 int ATParser::_getc() {
geky 0:c741e144517c 43 Timer timer;
geky 0:c741e144517c 44 timer.start();
geky 0:c741e144517c 45
geky 0:c741e144517c 46 while (true) {
geky 0:c741e144517c 47 if (_serial->readable())
geky 0:c741e144517c 48 return _serial->getc();
geky 0:c741e144517c 49
geky 0:c741e144517c 50 if (timer.read_ms() > _timeout)
geky 0:c741e144517c 51 return -1;
geky 0:c741e144517c 52 }
geky 0:c741e144517c 53 }
geky 0:c741e144517c 54
geky 0:c741e144517c 55 void ATParser::_flush() {
geky 0:c741e144517c 56 while (_serial->readable())
geky 0:c741e144517c 57 _serial->getc();
geky 0:c741e144517c 58 }
geky 0:c741e144517c 59
geky 1:66a14afe650a 60
geky 0:c741e144517c 61 // getline/putline handling with timeouts/bounds checking
geky 1:66a14afe650a 62 bool ATParser::_putline(const char *line) {
geky 0:c741e144517c 63 for (int i = 0; line[i]; i++) {
geky 0:c741e144517c 64 if (_putc(line[i]) < 0)
geky 0:c741e144517c 65 return false;
geky 0:c741e144517c 66 }
geky 0:c741e144517c 67
geky 0:c741e144517c 68 // Finish with newline
geky 3:32915b9467d2 69 for (int i = 0; _delimiter[i]; i++) {
geky 3:32915b9467d2 70 if (_putc(_delimiter[i]) < 0)
geky 3:32915b9467d2 71 return false;
geky 3:32915b9467d2 72 }
geky 0:c741e144517c 73
geky 0:c741e144517c 74 #ifdef AT_ECHO
geky 0:c741e144517c 75 printf("AT> %s\r\n", line);
geky 0:c741e144517c 76 #endif
geky 1:66a14afe650a 77
geky 0:c741e144517c 78 return true;
geky 0:c741e144517c 79 }
geky 0:c741e144517c 80
geky 1:66a14afe650a 81 bool ATParser::_getline(char *line, int size) {
geky 1:66a14afe650a 82 int i = 0;
geky 1:66a14afe650a 83
geky 1:66a14afe650a 84 while (i < size) {
geky 0:c741e144517c 85 int c = _getc();
geky 1:66a14afe650a 86 if (c < 0)
geky 1:66a14afe650a 87 return false;
geky 3:32915b9467d2 88
geky 3:32915b9467d2 89 line[i++] = c;
geky 0:c741e144517c 90
geky 1:66a14afe650a 91 // Finish when we hit a newline
geky 3:32915b9467d2 92 if (memcmp(&line[i-_delim_size], _delimiter, _delim_size) == 0) {
geky 3:32915b9467d2 93 line[i-_delim_size] = 0;
geky 3:32915b9467d2 94 #ifdef AT_ECHO
geky 0:c741e144517c 95 printf("AT< %s\r\n", line);
geky 0:c741e144517c 96 #endif
geky 0:c741e144517c 97 return true;
geky 0:c741e144517c 98 }
geky 0:c741e144517c 99 }
geky 0:c741e144517c 100
geky 0:c741e144517c 101 // Ran out of space
geky 0:c741e144517c 102 return false;
geky 1:66a14afe650a 103 }
geky 0:c741e144517c 104
geky 0:c741e144517c 105
geky 0:c741e144517c 106 bool ATParser::command(const char *command, const char *response, ...) {
geky 0:c741e144517c 107 va_list args;
geky 0:c741e144517c 108 va_start(args, response);
geky 0:c741e144517c 109
geky 0:c741e144517c 110 _flush();
geky 0:c741e144517c 111
geky 0:c741e144517c 112 // Create and send command
geky 3:32915b9467d2 113 if (command) {
geky 3:32915b9467d2 114 if (vsprintf(_buffer, command, args) < 0 ||
geky 3:32915b9467d2 115 !_putline(_buffer)) {
geky 3:32915b9467d2 116 va_end(args);
geky 3:32915b9467d2 117 return false;
geky 3:32915b9467d2 118 }
geky 0:c741e144517c 119 }
geky 0:c741e144517c 120
geky 1:66a14afe650a 121 // Iterate through each line in the expected response
geky 1:66a14afe650a 122 while (response && response[0]) {
geky 1:66a14afe650a 123 // Since response is const, we need to copy it into our buffer to
geky 1:66a14afe650a 124 // add the line's null terminator and clobber value matches with asterisks.
geky 1:66a14afe650a 125 //
geky 1:66a14afe650a 126 // We just use the beginning of the buffer to avoid unecessary allocations.
geky 1:66a14afe650a 127 int i = 0;
geky 1:66a14afe650a 128 int offset = 0;
geky 1:66a14afe650a 129
geky 1:66a14afe650a 130 while (response[i]) {
geky 3:32915b9467d2 131 if (memcmp(&response[i-_delim_size], _delimiter, _delim_size) == 0) {
geky 3:32915b9467d2 132 i += _delim_size;
geky 1:66a14afe650a 133 break;
geky 1:66a14afe650a 134 } else if (response[i] == '%' &&
geky 1:66a14afe650a 135 response[i+1] != '%' &&
geky 1:66a14afe650a 136 response[i+1] != '*') {
geky 1:66a14afe650a 137 _buffer[offset++] = '%';
geky 1:66a14afe650a 138 _buffer[offset++] = '*';
geky 3:32915b9467d2 139 i++;
geky 1:66a14afe650a 140 } else {
geky 1:66a14afe650a 141 _buffer[offset++] = response[i++];
geky 1:66a14afe650a 142 }
geky 1:66a14afe650a 143 }
geky 1:66a14afe650a 144
geky 1:66a14afe650a 145 // Scanf has very poor support for catching errors
geky 1:66a14afe650a 146 // fortunately, we can abuse the %n specifier to determine
geky 1:66a14afe650a 147 // if the entire string was matched.
geky 1:66a14afe650a 148 _buffer[offset++] = '%';
geky 1:66a14afe650a 149 _buffer[offset++] = 'n';
geky 1:66a14afe650a 150 _buffer[offset++] = 0;
geky 1:66a14afe650a 151
geky 1:66a14afe650a 152 // To workaround scanf's lack of error reporting, we actually
geky 1:66a14afe650a 153 // make two passes. One checks the validity with the modified
geky 1:66a14afe650a 154 // format string that only stores the matched characters (%n).
geky 1:66a14afe650a 155 // The other reads in the actual matched values.
geky 1:66a14afe650a 156 //
geky 1:66a14afe650a 157 // We keep trying the match until we succeed or some other error
geky 1:66a14afe650a 158 // derails us.
geky 1:66a14afe650a 159 while (true) {
geky 1:66a14afe650a 160 // Recieve response
geky 1:66a14afe650a 161 if (!_getline(_buffer+offset, _buffer_size-offset)) {
geky 1:66a14afe650a 162 va_end(args);
geky 1:66a14afe650a 163 return false;
geky 1:66a14afe650a 164 }
geky 1:66a14afe650a 165
geky 2:4d68f546861c 166 int count = -1;
geky 1:66a14afe650a 167 sscanf(_buffer+offset, _buffer, &count);
geky 1:66a14afe650a 168
geky 1:66a14afe650a 169 // We only succeed if all characters in the response is matched
geky 2:4d68f546861c 170 if (count >= 0 && (_buffer+offset)[count] == 0) {
geky 1:66a14afe650a 171 // Reuse the front end of the buffer
geky 1:66a14afe650a 172 int j;
geky 1:66a14afe650a 173 for (j = 0; j < i; j++) {
geky 1:66a14afe650a 174 _buffer[j] = response[j];
geky 1:66a14afe650a 175 }
geky 1:66a14afe650a 176 _buffer[j] = 0;
geky 1:66a14afe650a 177
geky 1:66a14afe650a 178 // Store the found results
geky 1:66a14afe650a 179 vsscanf(_buffer+offset, _buffer, args);
geky 1:66a14afe650a 180
geky 1:66a14afe650a 181 // Jump to next line and continue parsing
geky 1:66a14afe650a 182 response += i;
geky 1:66a14afe650a 183 break;
geky 1:66a14afe650a 184 }
geky 1:66a14afe650a 185 }
geky 0:c741e144517c 186 }
geky 0:c741e144517c 187
geky 0:c741e144517c 188 va_end(args);
geky 0:c741e144517c 189 return true;
geky 0:c741e144517c 190 }