Dependencies:
BufferedSerial
ATParser.cpp
- Committer:
- geky
- Date:
- 2015-07-17
- Revision:
- 3:32915b9467d2
- Parent:
- 2:4d68f546861c
- Child:
- 4:38acbd6f9d9e
File content as of revision 3:32915b9467d2:
/* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @section DESCRIPTION
*
* Parser for the AT command syntax
*
*/
#include "ATParser.h"
#include <cstdarg>
// This can be defined to assist in debugging
#define AT_ECHO 1
// getc/putc handling with timeouts
int ATParser::_putc(char c) {
Timer timer;
timer.start();
while (true) {
if (_serial->writeable())
return _serial->putc(c);
if (timer.read_ms() > _timeout)
return -1;
}
}
int ATParser::_getc() {
Timer timer;
timer.start();
while (true) {
if (_serial->readable())
return _serial->getc();
if (timer.read_ms() > _timeout)
return -1;
}
}
void ATParser::_flush() {
while (_serial->readable())
_serial->getc();
}
// getline/putline handling with timeouts/bounds checking
bool ATParser::_putline(const char *line) {
for (int i = 0; line[i]; i++) {
if (_putc(line[i]) < 0)
return false;
}
// Finish with newline
for (int i = 0; _delimiter[i]; i++) {
if (_putc(_delimiter[i]) < 0)
return false;
}
#ifdef AT_ECHO
printf("AT> %s\r\n", line);
#endif
return true;
}
bool ATParser::_getline(char *line, int size) {
int i = 0;
while (i < size) {
int c = _getc();
if (c < 0)
return false;
line[i++] = c;
// Finish when we hit a newline
if (memcmp(&line[i-_delim_size], _delimiter, _delim_size) == 0) {
line[i-_delim_size] = 0;
#ifdef AT_ECHO
printf("AT< %s\r\n", line);
#endif
return true;
}
}
// Ran out of space
return false;
}
bool ATParser::command(const char *command, const char *response, ...) {
va_list args;
va_start(args, response);
_flush();
// Create and send command
if (command) {
if (vsprintf(_buffer, command, args) < 0 ||
!_putline(_buffer)) {
va_end(args);
return false;
}
}
// Iterate through each line in the expected response
while (response && response[0]) {
// Since response is const, we need to copy it into our buffer to
// add the line's null terminator and clobber value matches with asterisks.
//
// We just use the beginning of the buffer to avoid unecessary allocations.
int i = 0;
int offset = 0;
while (response[i]) {
if (memcmp(&response[i-_delim_size], _delimiter, _delim_size) == 0) {
i += _delim_size;
break;
} else if (response[i] == '%' &&
response[i+1] != '%' &&
response[i+1] != '*') {
_buffer[offset++] = '%';
_buffer[offset++] = '*';
i++;
} else {
_buffer[offset++] = response[i++];
}
}
// Scanf has very poor support for catching errors
// fortunately, we can abuse the %n specifier to determine
// if the entire string was matched.
_buffer[offset++] = '%';
_buffer[offset++] = 'n';
_buffer[offset++] = 0;
// To workaround scanf's lack of error reporting, we actually
// make two passes. One checks the validity with the modified
// format string that only stores the matched characters (%n).
// The other reads in the actual matched values.
//
// We keep trying the match until we succeed or some other error
// derails us.
while (true) {
// Recieve response
if (!_getline(_buffer+offset, _buffer_size-offset)) {
va_end(args);
return false;
}
int count = -1;
sscanf(_buffer+offset, _buffer, &count);
// We only succeed if all characters in the response is matched
if (count >= 0 && (_buffer+offset)[count] == 0) {
// Reuse the front end of the buffer
int j;
for (j = 0; j < i; j++) {
_buffer[j] = response[j];
}
_buffer[j] = 0;
// Store the found results
vsscanf(_buffer+offset, _buffer, args);
// Jump to next line and continue parsing
response += i;
break;
}
}
}
va_end(args);
return true;
}