ssdccv

Dependencies:   BufferedSerial

Files at this revision

API Documentation at this revision

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

ATParser.cpp Show annotated file Show diff for this revision Revisions of this file
ATParser.h Show annotated file Show diff for this revision Revisions of this file
BufferedSerial.lib Show annotated file Show diff for this revision Revisions of this file
--- 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