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 yield(); 38 } while(millis() - startMillis < _timeout); 39 return -1; // -1 indicates timeout 40 } 41 42 // private method to peek stream with timeout 43 int Stream::timedPeek() 44 { 45 int c; 46 unsigned long startMillis = millis(); 47 do { 48 c = peek(); 49 if (c >= 0) return c; 50 yield(); 51 } while(millis() - startMillis < _timeout); 52 return -1; // -1 indicates timeout 53 } 54 55 // returns peek of the next digit in the stream or -1 if timeout 56 // discards non-numeric characters 57 int Stream::peekNextDigit() 58 { 59 int c; 60 while (1) { 61 c = timedPeek(); 62 if (c < 0) return c; // timeout 63 if (c == '-') return c; 64 if (c >= '0' && c <= '9') return c; 65 read(); // discard non-numeric 66 } 67 } 68 69 // Public Methods 70 ////////////////////////////////////////////////////////////// 71 72 void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait 73 { 74 _timeout = timeout; 75 } 76 77 // find returns true if the target string is found 78 bool Stream::find(char *target) 79 { 80 return findUntil(target, NULL); 81 } 82 83 // reads data from the stream until the target string of given length is found 84 // returns true if target string is found, false if timed out 85 bool Stream::find(char *target, size_t length) 86 { 87 return findUntil(target, length, NULL, 0); 88 } 89 90 // as find but search ends if the terminator string is found 91 bool Stream::findUntil(char *target, char *terminator) 92 { 93 return findUntil(target, strlen(target), terminator, strlen(terminator)); 94 } 95 96 // reads data from the stream until the target string of the given length is found 97 // search terminated if the terminator string is found 98 // returns true if target string is found, false if terminated or timed out 99 bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen) 100 { 101 size_t index = 0; // maximum target string length is 64k bytes! 102 size_t termIndex = 0; 103 int c; 104 105 if( *target == 0) 106 return true; // return true if target is a null string 107 while( (c = timedRead()) > 0){ 108 if( c == target[index]){ 109 //////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1); 110 if(++index >= targetLen){ // return true if all chars in the target match 111 return true; 112 } 113 } 114 else{ 115 index = 0; // reset index if any char does not match 116 } 117 if(termLen > 0 && c == terminator[termIndex]){ 118 if(++termIndex >= termLen) 119 return false; // return false if terminate string found before target string 120 } 121 else 122 termIndex = 0; 123 } 124 return false; 125 } 126 127 128 // returns the first valid (long) integer value from the current position. 129 // initial characters that are not digits (or the minus sign) are skipped 130 // function is terminated by the first character that is not a digit. 131 long Stream::parseInt() 132 { 133 return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout) 134 } 135 136 // as above but a given skipChar is ignored 137 // this allows format characters (typically commas) in values to be ignored 138 long Stream::parseInt(char skipChar) 139 { 140 boolean isNegative = false; 141 long value = 0; 142 int c; 143 144 c = peekNextDigit(); 145 // ignore non numeric leading characters 146 if(c < 0) 147 return 0; // zero returned if timeout 148 149 do{ 150 if(c == skipChar) 151 ; // ignore this charactor 152 else if(c == '-') 153 isNegative = true; 154 else if(c >= '0' && c <= '9') // is c a digit? 155 value = value * 10 + c - '0'; 156 read(); // consume the character we got with peek 157 c = timedPeek(); 158 } 159 while( (c >= '0' && c <= '9') || c == skipChar ); 160 161 if(isNegative) 162 value = -value; 163 return value; 164 } 165 166 167 // as parseInt but returns a floating point value 168 float Stream::parseFloat() 169 { 170 return parseFloat(NO_SKIP_CHAR); 171 } 172 173 // as above but the given skipChar is ignored 174 // this allows format characters (typically commas) in values to be ignored 175 float Stream::parseFloat(char skipChar){ 176 boolean isNegative = false; 177 boolean isFraction = false; 178 long value = 0; 179 char c; 180 float fraction = 1.0; 181 182 c = peekNextDigit(); 183 // ignore non numeric leading characters 184 if(c < 0) 185 return 0; // zero returned if timeout 186 187 do{ 188 if(c == skipChar) 189 ; // ignore 190 else if(c == '-') 191 isNegative = true; 192 else if (c == '.') 193 isFraction = true; 194 else if(c >= '0' && c <= '9') { // is c a digit? 195 value = value * 10 + c - '0'; 196 if(isFraction) 197 fraction *= 0.1; 198 } 199 read(); // consume the character we got with peek 200 c = timedPeek(); 201 } 202 while( (c >= '0' && c <= '9') || c == '.' || c == skipChar ); 203 204 if(isNegative) 205 value = -value; 206 if(isFraction) 207 return value * fraction; 208 else 209 return value; 210 } 211 212 // read characters from stream into buffer 213 // terminates if length characters have been read, or timeout (see setTimeout) 214 // returns the number of characters placed in the buffer 215 // the buffer is NOT null terminated. 216 // 217 size_t Stream::readBytes(char *buffer, size_t length) 218 { 219 size_t count = 0; 220 while (count < length) { 221 int c = timedRead(); 222 if (c < 0) { 223 setReadError(); 224 break; 225 } 226 *buffer++ = (char)c; 227 count++; 228 } 229 return count; 230 } 231 232 233 // as readBytes with terminator character 234 // terminates if length characters have been read, timeout, or if the terminator character detected 235 // returns the number of characters placed in the buffer (0 means no valid data found) 236 237 size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) 238 { 239 if (length < 1) return 0; 240 length--; 241 size_t index = 0; 242 while (index < length) { 243 int c = timedRead(); 244 if (c == terminator) break; 245 if (c < 0) { 246 setReadError(); 247 break; 248 } 249 *buffer++ = (char)c; 250 index++; 251 } 252 *buffer = 0; 253 return index; // return number of characters, not including null terminator 254 } 255 256 String Stream::readString(size_t max) 257 { 258 String str; 259 size_t length = str.length(); 260 while (length < max) { 261 int c = timedRead(); 262 if (c < 0) { 263 setReadError(); 264 break; // timeout 265 } 266 if (c == 0) break; 267 str += (char)c; 268 } 269 return str; 270 } 271 272 String Stream::readStringUntil(char terminator, size_t max) 273 { 274 String str; 275 size_t length = str.length(); 276 while (length < max) { 277 int c = timedRead(); 278 if (c < 0) { 279 setReadError(); 280 break; // timeout 281 } 282 if (c == 0 || c == terminator) break; 283 str += (char)c; 284 } 285 return str; 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 311 312