ssdccv
Revision 1:66a14afe650a, committed 2015-07-16
- Comitter:
- geky
- Date:
- Thu Jul 16 20:42:44 2015 +0000
- Parent:
- 0:c741e144517c
- Child:
- 2:4d68f546861c
- Commit message:
- Worked around scanf's lack of error reporting; ; This required a rather roundabout method of performing two scanf passes, one with clobbered matches %n, and the other with the actual value matches.; ; Also the parser now scans multiple lines.
Changed in this revision
--- a/ATParser.cpp Wed Jul 15 22:39:25 2015 +0000 +++ b/ATParser.cpp Thu Jul 16 20:42:44 2015 +0000 @@ -22,7 +22,7 @@ #include <cstdarg> // This can be defined to assist in debugging -#define AT_ECHO 1 +//#define AT_ECHO 1 // getc/putc handling with timeouts @@ -57,50 +57,53 @@ _serial->getc(); } + // getline/putline handling with timeouts/bounds checking -bool ATParser::_putline(const char *line) { +bool ATParser::_putline(const char *line) { for (int i = 0; line[i]; i++) { if (_putc(line[i]) < 0) return false; } // Finish with newline - if (_putc('\r') < 0 || - _putc('\n') < 0) + if (_putc('\r') < 0 || _putc('\n') < 0) return false; #ifdef AT_ECHO printf("AT> %s\r\n", line); #endif - + return true; } -bool ATParser::_getline(int size, char *line) { - for (int i = 0; i < size; i++) { +bool ATParser::_getline(char *line, int size) { + int i = 0; + + while (i < size) { int c = _getc(); - if (c < 0) - return false; + if (c < 0) + return false; - // Finish if newline - if (c == '\r') { - if (_getc() != '\n') - return false; + // Finish when we hit a newline + if (c == '\r' || c == '\n') { + // Only handle newlines on \n + if (c != '\n') + continue; - line[i] = 0; + line[i++] = 0; #ifdef AT_ECHO printf("AT< %s\r\n", line); #endif return true; } - line[i] = c; + line[i++] = c; } // Ran out of space return false; -} +} bool ATParser::command(const char *command, const char *response, ...) { @@ -115,20 +118,76 @@ va_end(args); return false; } - - // Determine number of parameters - // this is needed for scanf's funky error signaling - int params = 0; - for (int i = 0; response[i]; i++) { - if (response[i] == '%' && response[i+1] != '%') - params++; - } - // Recieve and parse response - if (!_getline(_buffer_size, _buffer) || - vsscanf(_buffer, response, args) < params) { - 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]) { + // Only handle newlines on \n + if (response[i] == '\n') { + i++; + break; + } else if (response[i] == '\r') { + i++; + } else if (response[i] == '%' && + response[i+1] != '%' && + response[i+1] != '*') { + i++; + + _buffer[offset++] = '%'; + _buffer[offset++] = '*'; + } 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; + sscanf(_buffer+offset, _buffer, &count); + + // We only succeed if all characters in the response is matched + if ((_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);
--- a/ATParser.h Wed Jul 15 22:39:25 2015 +0000 +++ b/ATParser.h Thu Jul 16 20:42:44 2015 +0000 @@ -19,6 +19,9 @@ */ #include "mbed.h" +#include <stdarg.h> + +#include "BufferedSerial.h" /** @@ -27,7 +30,7 @@ class ATParser { private: // Serial information - RawSerial *_serial; + BufferedSerial *_serial; int _buffer_size; char *_buffer; int _timeout; @@ -42,16 +45,18 @@ // Helper methods for reading/writing lines with // timeout and buffer limitations bool _putline(const char *line); - bool _getline(int size, char *line); + bool _getline(char *line, int size); public: /** * Constructor * * @param serial serial interface to use for AT commands + * @param buffer_size size of internal buffer for transaction * @param timeout timeout of the connection + * @param echo flag to indicate if an echo of sent characters should be expected */ - ATParser(RawSerial *serial, int buffer_size = 256, int timeout = 3000) : + ATParser(BufferedSerial *serial, int buffer_size = 256, int timeout = 3000) : _serial(serial), _buffer_size(buffer_size), _timeout(timeout) { @@ -76,13 +81,21 @@ /** * Issue AT commands with specified command and expected response + * Uses printf/scanf like format strings to be able to parse the results + * + * Here are some examples: + * @code + * at.command("AT", "OK"); + * at.command("AT+CWMODE=%d", "OK", 3); + * at.command("AT+CWMODE?", "+CWMODE:%d OK", &result); + * @endcode * * @param command printf-like format string of command to send * @param response scanf-like format string of response to parse * @param ... all printf-like arguments to insert into command followed by * all scanf-like pointers to destinations for response values - * @return true if response is successfully matched + * @return true only if response is successfully matched */ bool command(const char *command, const char *response, ...); }; - \ No newline at end of file +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BufferedSerial.lib Thu Jul 16 20:42:44 2015 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/sam_grove/code/BufferedSerial/#9ee15ae3d1a3