Mbed Cloud example program for workshop in W27 2018.

Dependencies:   MMA7660 LM75B

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATParser.cpp Source File

ATParser.cpp

00001 /* Copyright (c) 2015 ARM Limited
00002  *
00003  * Licensed under the Apache License, Version 2.0 (the "License");
00004  * you may not use this file except in compliance with the License.
00005  * You may obtain a copy of the License at
00006  *
00007  *     http://www.apache.org/licenses/LICENSE-2.0
00008  *
00009  * Unless required by applicable law or agreed to in writing, software
00010  * distributed under the License is distributed on an "AS IS" BASIS,
00011  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012  * See the License for the specific language governing permissions and
00013  * limitations under the License.
00014  *
00015  * @section DESCRIPTION
00016  *
00017  * Parser for the AT command syntax
00018  *
00019  */
00020 
00021 #include "ATParser.h"
00022 #include "mbed_debug.h"
00023 
00024 #ifdef LF
00025 #undef LF
00026 #define LF  10
00027 #else
00028 #define LF  10
00029 #endif
00030 
00031 #ifdef CR
00032 #undef CR
00033 #define CR  13
00034 #else
00035 #define CR  13
00036 #endif
00037 #define MIN(a,b) (((a)<(b))?(a):(b))
00038 
00039 #define dbg_on 0
00040 //#define TRACE_AT_DATA 1
00041 
00042 // getc/putc handling with timeouts
00043 int ATParser::putc(char c)
00044 {
00045     return _serial_spi->putc(c);
00046 }
00047 
00048 int ATParser::getc()
00049 {
00050     return _serial_spi->getc();
00051 }
00052 
00053 void ATParser::flush()
00054 {
00055     _bufferMutex.lock();
00056     while (_serial_spi->readable()) {
00057         _serial_spi->getc();
00058     }
00059     _bufferMutex.unlock();
00060 }
00061 
00062 // read/write handling with timeouts
00063 int ATParser::write(const char *data, int size_of_data, int size_in_buff)
00064 {
00065     int i = 0;
00066     _bufferMutex.lock();
00067     for ( ; i < size_of_data; i++) {
00068         if (putc(data[i]) < 0) {
00069             _bufferMutex.unlock();
00070             return -1;
00071         }
00072     }
00073 
00074     _serial_spi->buffsend(size_of_data + size_in_buff);
00075     _bufferMutex.unlock();
00076     return (size_of_data + size_in_buff);
00077 }
00078 
00079 int ATParser::read(char *data)
00080 {
00081     int readsize;
00082     int i = 0;
00083 
00084     _bufferMutex.lock();
00085 
00086     //this->flush();
00087     if(!_serial_spi->readable()) {
00088         readsize = _serial_spi->read();
00089     } else {
00090         error("Pending data when reading from WIFI\r\n");
00091         return -1;
00092     }
00093 
00094     debug_if(dbg_on, "Avail in SPI %d\r\n", readsize);
00095 
00096     if ( readsize < 0) {
00097         _bufferMutex.unlock();
00098         return -1;
00099     }
00100 
00101     for (i = 0 ; i < readsize; i++) {
00102        int c = getc();
00103        if (c < 0) {
00104            _bufferMutex.unlock();
00105            return -1;
00106        }
00107        data[i] = c;
00108     }
00109 
00110 #if TRACE_AT_DATA
00111     debug_if(dbg_on, "AT<< %d BYTES\r\n", readsize);
00112     for (i = 0; i < readsize; i++) {
00113          debug_if(dbg_on, "%2X ", data[i]);
00114     }
00115     debug_if(dbg_on, "\r\n");
00116 #endif
00117 
00118     _bufferMutex.unlock();
00119 
00120     return (readsize);
00121 }
00122 
00123 // printf/scanf handling
00124 int ATParser::vprintf(const char *format, va_list args)
00125 {
00126     _bufferMutex.lock();
00127     if (vsprintf(_buffer, format, args) < 0) {
00128         _bufferMutex.unlock();
00129         return false;
00130     }
00131 
00132     int i = 0;
00133     for ( ; _buffer[i]; i++) {
00134         if (putc(_buffer[i]) < 0) {
00135             _bufferMutex.unlock();
00136             return -1;
00137         }
00138     }
00139     _bufferMutex.unlock();
00140 
00141     return i;
00142 }
00143 
00144 int ATParser::vscanf(const char *format, va_list args)
00145 {
00146     // Since format is const, we need to copy it into our buffer to
00147     // add the line's null terminator and clobber value-matches with asterisks.
00148     //
00149     // We just use the beginning of the buffer to avoid unnecessary allocations.
00150     int i = 0;
00151     int offset = 0;
00152 
00153     _bufferMutex.lock();
00154 
00155     while (format[i]) {
00156         if (format[i] == '%' && format[i+1] != '%' && format[i+1] != '*') {
00157             _buffer[offset++] = '%';
00158             _buffer[offset++] = '*';
00159             i++;
00160         } else {
00161             _buffer[offset++] = format[i++];
00162         }
00163     }
00164 
00165     // Scanf has very poor support for catching errors
00166     // fortunately, we can abuse the %n specifier to determine
00167     // if the entire string was matched.
00168     _buffer[offset++] = '%';
00169     _buffer[offset++] = 'n';
00170     _buffer[offset++] = 0;
00171 
00172     // To workaround scanf's lack of error reporting, we actually
00173     // make two passes. One checks the validity with the modified
00174     // format string that only stores the matched characters (%n).
00175     // The other reads in the actual matched values.
00176     //
00177     // We keep trying the match until we succeed or some other error
00178     // derails us.
00179     int j = 0;
00180 
00181     while (true) {
00182         // Ran out of space
00183         if (j+1 >= _buffer_size - offset) {
00184             _bufferMutex.unlock();
00185             return false;
00186         }
00187         // Recieve next character
00188         int c = getc();
00189         if (c < 0) {
00190             _bufferMutex.unlock();
00191             return -1;
00192         }
00193         _buffer[offset + j++] = c;
00194         _buffer[offset + j] = 0;
00195 
00196         // Check for match
00197         int count = -1;
00198         sscanf(_buffer+offset, _buffer, &count);
00199 
00200         // We only succeed if all characters in the response are matched
00201         if (count == j) {
00202             // Store the found results
00203             vsscanf(_buffer+offset, format, args);
00204             _bufferMutex.unlock();
00205             return j;
00206         }
00207     }
00208 }
00209 
00210 
00211 // Command parsing with line handling
00212 bool ATParser::vsend(const char *command, va_list args)
00213 {
00214     int i=0, j=0;
00215     _bufferMutex.lock();
00216     // Create and send command
00217     if (vsprintf(_buffer, command, args) < 0) {
00218         _bufferMutex.unlock();
00219         return false;
00220     }
00221     /* get buffer length */
00222     for (i = 0; _buffer[i]; i++) {
00223     }
00224 
00225     for (j=0; _delimiter[j]; j++) {
00226         _buffer[i+j] = _delimiter[j];
00227     }
00228     _buffer[i+j]=0; // only to get a clean debug log
00229     
00230     bool ret = !(_serial_spi->buffwrite(_buffer, i+j) < 0);
00231 
00232     debug_if(dbg_on, "AT> %s\n", _buffer);
00233     _bufferMutex.unlock();
00234     return ret;
00235 }
00236 
00237 bool ATParser::vrecv(const char *response, va_list args)
00238 {
00239     _bufferMutex.lock();
00240     /* Read from the wifi module, fill _rxbuffer */
00241     //this->flush();
00242     if(!_serial_spi->readable()) {
00243          debug_if(dbg_on, "NO DATA, read again\r\n");
00244         if (_serial_spi->read() < 0) {
00245             return false;
00246         }
00247     } else {
00248          debug_if(dbg_on, "Pending data\r\n");
00249     }
00250 restart:
00251     _aborted = false;
00252     // Iterate through each line in the expected response
00253     while (response[0]) {
00254         // Since response is const, we need to copy it into our buffer to
00255         // add the line's null terminator and clobber value-matches with asterisks.
00256         //
00257         // We just use the beginning of the buffer to avoid unnecessary allocations.
00258         int i = 0;
00259         int offset = 0;
00260         bool whole_line_wanted = false;
00261 
00262         while (response[i]) {
00263             if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') {
00264                 _buffer[offset++] = '%';
00265                 _buffer[offset++] = '*';
00266                 i++;
00267             } else {
00268                 _buffer[offset++] = response[i++];
00269                 // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification
00270                 if (response[i - 1] == '\n' && !(i >= 3 && response[i-3] == '[' && response[i-2] == '^')) {
00271                     whole_line_wanted = true;
00272                     break;
00273                 }
00274             }
00275         }
00276 
00277         // Scanf has very poor support for catching errors
00278         // fortunately, we can abuse the %n specifier to determine
00279         // if the entire string was matched.
00280         _buffer[offset++] = '%';
00281         _buffer[offset++] = 'n';
00282         _buffer[offset++] = 0;
00283 
00284         debug_if(dbg_on, "AT? ====%s====\n", _buffer);
00285         // To workaround scanf's lack of error reporting, we actually
00286         // make two passes. One checks the validity with the modified
00287         // format string that only stores the matched characters (%n).
00288         // The other reads in the actual matched values.
00289         //
00290         // We keep trying the match until we succeed or some other error
00291         // derails us.
00292         int j = 0;
00293 
00294         while (true) {
00295             // Recieve next character
00296             int c = getc();
00297             if (c < 0) {
00298                 debug_if(dbg_on, "AT(Timeout)\n");
00299                 _bufferMutex.unlock();
00300                 return false;
00301             }
00302 
00303 #if TRACE_AT_DATA
00304              debug_if(dbg_on, "%2X ", c);
00305 #endif
00306             _buffer[offset + j++] = c;
00307             _buffer[offset + j] = 0;
00308 
00309             // Check for oob data
00310             for (struct oob *oob = _oobs; oob; oob = oob->next) {
00311                 if ((unsigned)j == oob->len && memcmp(
00312                         oob->prefix, _buffer+offset, oob->len) == 0) {
00313                     debug_if(dbg_on, "AT! %s\n", oob->prefix);
00314                     oob->cb();
00315 
00316                     if (_aborted) {
00317                         debug_if(dbg_on, "AT(Aborted)\n");
00318                         _bufferMutex.unlock();
00319                         return false;
00320                     }
00321                     // oob may have corrupted non-reentrant buffer,
00322                     // so we need to set it up again
00323                     goto restart;
00324                 }
00325             }
00326 
00327             // Check for match
00328             int count = -1;
00329             if (whole_line_wanted && c != '\n') {
00330                 // Don't attempt scanning until we get delimiter if they included it in format
00331                 // This allows recv("Foo: %s\n") to work, and not match with just the first character of a string
00332                 // (scanf does not itself match whitespace in its format string, so \n is not significant to it)
00333             } else {
00334                 sscanf(_buffer+offset, _buffer, &count);
00335             }
00336 
00337             // We only succeed if all characters in the response are matched
00338             if (count == j) {
00339                 debug_if(dbg_on, "AT= ====%s====\n", _buffer + offset);
00340                 // Reuse the front end of the buffer
00341                 memcpy(_buffer, response, i);
00342                 _buffer[i] = 0;
00343 
00344                 // Store the found results
00345                 vsscanf(_buffer+offset, _buffer, args);
00346 
00347                 // Jump to next line and continue parsing
00348                 response += i;
00349                 break;
00350             }
00351 
00352             // Clear the buffer when we hit a newline or ran out of space
00353             // running out of space usually means we ran into binary data
00354             if ((c == '\n') ) {
00355                 debug_if(dbg_on, "New line AT<<< %s", _buffer+offset);
00356                 j = 0;
00357             }
00358             if ((j + 1 >= (_buffer_size - offset))) {
00359 
00360                 debug_if(dbg_on, "Out of space AT<<< %s, j=%d", _buffer+offset, j);
00361                 j = 0;
00362             }
00363         }
00364     }
00365 
00366     _bufferMutex.unlock();
00367 
00368     return true;
00369 }
00370 
00371 
00372 // Mapping to vararg functions
00373 int ATParser::printf(const char *format, ...)
00374 {
00375     va_list args;
00376     va_start(args, format);
00377     int res = vprintf(format, args);
00378     va_end(args);
00379     return res;
00380 }
00381 
00382 int ATParser::scanf(const char *format, ...)
00383 {
00384     va_list args;
00385     va_start(args, format);
00386     int res = vscanf(format, args);
00387     va_end(args);
00388     return res;
00389 }
00390 
00391 bool ATParser::send(const char *command, ...)
00392 {
00393     va_list args;
00394     va_start(args, command);
00395     bool res = vsend(command, args);
00396     va_end(args);
00397     return res;
00398 }
00399 
00400 bool ATParser::recv(const char *response, ...)
00401 {
00402     va_list args;
00403     va_start(args, response);
00404     bool res = vrecv(response, args);
00405     va_end(args);
00406     return res;
00407 }
00408 
00409 
00410 // oob registration
00411 void ATParser::oob(const char *prefix, Callback<void()> cb)
00412 {
00413     struct oob *oob = new struct oob;
00414     oob->len = strlen(prefix);
00415     oob->prefix = prefix;
00416     oob->cb = cb;
00417     oob->next = _oobs;
00418     _oobs = oob;
00419 }
00420 
00421 void ATParser::abort()
00422 {
00423     _aborted = true;
00424 }
00425 
00426 
00427