Stream.cpp
1 /* 2 Stream.cpp - adds parsing methods to Stream class 3 Copyright (c) 2008 David A. Mellis. All right reserved. 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19 Created July 2011 20 parsing functions based on TextFinder library by Michael Margolis 21 */ 22 23 #include "Arduino.h" 24 #include "Stream.h" 25 26 #define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait 27 #define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field 28 29 // private method to read stream with timeout 30 int Stream::timedRead() 31 { 32 int c; 33 unsigned long startMillis = millis(); 34 do { 35 c = read(); 36 if (c >= 0) return c; 37 } while(millis() - startMillis < _timeout); 38 return -1; // -1 indicates timeout 39 } 40 41 // private method to peek stream with timeout 42 int Stream::timedPeek() 43 { 44 int c; 45 unsigned long startMillis = millis(); 46 do { 47 c = peek(); 48 if (c >= 0) return c; 49 } while(millis() - startMillis < _timeout); 50 return -1; // -1 indicates timeout 51 } 52 53 // returns peek of the next digit in the stream or -1 if timeout 54 // discards non-numeric characters 55 int Stream::peekNextDigit() 56 { 57 int c; 58 while (1) { 59 c = timedPeek(); 60 if (c < 0) return c; // timeout 61 if (c == '-') return c; 62 if (c >= '0' && c <= '9') return c; 63 read(); // discard non-numeric 64 } 65 } 66 67 // Public Methods 68 ////////////////////////////////////////////////////////////// 69 70 void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait 71 { 72 _timeout = timeout; 73 } 74 75 // find returns true if the target string is found 76 bool Stream::find(char *target) 77 { 78 return findUntil(target, NULL); 79 } 80 81 // reads data from the stream until the target string of given length is found 82 // returns true if target string is found, false if timed out 83 bool Stream::find(char *target, size_t length) 84 { 85 return findUntil(target, length, NULL, 0); 86 } 87 88 // as find but search ends if the terminator string is found 89 bool Stream::findUntil(char *target, char *terminator) 90 { 91 return findUntil(target, strlen(target), terminator, strlen(terminator)); 92 } 93 94 // reads data from the stream until the target string of the given length is found 95 // search terminated if the terminator string is found 96 // returns true if target string is found, false if terminated or timed out 97 bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen) 98 { 99 size_t index = 0; // maximum target string length is 64k bytes! 100 size_t termIndex = 0; 101 int c; 102 103 if( *target == 0) 104 return true; // return true if target is a null string 105 while( (c = timedRead()) > 0){ 106 if( c == target[index]){ 107 //////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1); 108 if(++index >= targetLen){ // return true if all chars in the target match 109 return true; 110 } 111 } 112 else{ 113 index = 0; // reset index if any char does not match 114 } 115 if(termLen > 0 && c == terminator[termIndex]){ 116 if(++termIndex >= termLen) 117 return false; // return false if terminate string found before target string 118 } 119 else 120 termIndex = 0; 121 } 122 return false; 123 } 124 125 126 // returns the first valid (long) integer value from the current position. 127 // initial characters that are not digits (or the minus sign) are skipped 128 // function is terminated by the first character that is not a digit. 129 long Stream::parseInt() 130 { 131 return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout) 132 } 133 134 // as above but a given skipChar is ignored 135 // this allows format characters (typically commas) in values to be ignored 136 long Stream::parseInt(char skipChar) 137 { 138 boolean isNegative = false; 139 long value = 0; 140 int c; 141 142 c = peekNextDigit(); 143 // ignore non numeric leading characters 144 if(c < 0) 145 return 0; // zero returned if timeout 146 147 do{ 148 if(c == skipChar) 149 ; // ignore this charactor 150 else if(c == '-') 151 isNegative = true; 152 else if(c >= '0' && c <= '9') // is c a digit? 153 value = value * 10 + c - '0'; 154 read(); // consume the character we got with peek 155 c = timedPeek(); 156 } 157 while( (c >= '0' && c <= '9') || c == skipChar ); 158 159 if(isNegative) 160 value = -value; 161 return value; 162 } 163 164 165 // as parseInt but returns a floating point value 166 float Stream::parseFloat() 167 { 168 return parseFloat(NO_SKIP_CHAR); 169 } 170 171 // as above but the given skipChar is ignored 172 // this allows format characters (typically commas) in values to be ignored 173 float Stream::parseFloat(char skipChar){ 174 boolean isNegative = false; 175 boolean isFraction = false; 176 long value = 0; 177 char c; 178 float fraction = 1.0; 179 180 c = peekNextDigit(); 181 // ignore non numeric leading characters 182 if(c < 0) 183 return 0; // zero returned if timeout 184 185 do{ 186 if(c == skipChar) 187 ; // ignore 188 else if(c == '-') 189 isNegative = true; 190 else if (c == '.') 191 isFraction = true; 192 else if(c >= '0' && c <= '9') { // is c a digit? 193 value = value * 10 + c - '0'; 194 if(isFraction) 195 fraction *= 0.1; 196 } 197 read(); // consume the character we got with peek 198 c = timedPeek(); 199 } 200 while( (c >= '0' && c <= '9') || c == '.' || c == skipChar ); 201 202 if(isNegative) 203 value = -value; 204 if(isFraction) 205 return value * fraction; 206 else 207 return value; 208 } 209 210 // read characters from stream into buffer 211 // terminates if length characters have been read, or timeout (see setTimeout) 212 // returns the number of characters placed in the buffer 213 // the buffer is NOT null terminated. 214 // 215 size_t Stream::readBytes(char *buffer, size_t length) 216 { 217 size_t count = 0; 218 while (count < length) { 219 int c = timedRead(); 220 if (c < 0) { 221 setReadError(); 222 break; 223 } 224 *buffer++ = (char)c; 225 count++; 226 } 227 return count; 228 } 229 230 231 // as readBytes with terminator character 232 // terminates if length characters have been read, timeout, or if the terminator character detected 233 // returns the number of characters placed in the buffer (0 means no valid data found) 234 235 size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) 236 { 237 if (length < 1) return 0; 238 length--; 239 size_t index = 0; 240 while (index < length) { 241 int c = timedRead(); 242 if (c == terminator) break; 243 if (c < 0) { 244 setReadError(); 245 break; 246 } 247 *buffer++ = (char)c; 248 index++; 249 } 250 *buffer = 0; 251 return index; // return number of characters, not including null terminator 252 } 253 254 String Stream::readString(size_t max) 255 { 256 String str; 257 size_t length = str.length(); 258 while (length < max) { 259 int c = timedRead(); 260 if (c < 0) { 261 setReadError(); 262 break; // timeout 263 } 264 if (c == 0) break; 265 str += (char)c; 266 } 267 return str; 268 } 269 270 String Stream::readStringUntil(char terminator, size_t max) 271 { 272 String str; 273 size_t length = str.length(); 274 while (length < max) { 275 int c = timedRead(); 276 if (c < 0) { 277 setReadError(); 278 break; // timeout 279 } 280 if (c == 0 || c == terminator) break; 281 str += (char)c; 282 } 283 return str; 284 } 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310