├── .gitignore ├── CSVFile.cpp ├── CSVFile.h ├── CSVFileConfig.h ├── CSVFileTest ├── CSVFileTest.ino ├── CSVFileTestInit.h ├── CSVFileTestPart1.h ├── CSVFileTestPart2.h ├── CSVFileTestPart3.h ├── CSVFileTestPart4.h ├── CSVFileTestPart5.h ├── CSVFileTestPart6.h ├── Config.h └── UnitTestUtils.h ├── LICENSE ├── README.md └── examples ├── Benchmark └── Benchmark.ino ├── CSVFileSize └── CSVFileSize.ino ├── CopyCSV └── CopyCSV.ino ├── DeleteLine └── DeleteLine.ino ├── EditCSV └── EditCSV.ino ├── HelloWorld └── HelloWorld.ino ├── Navigation └── Navigation.ino ├── NumberingLine └── NumberingLine.ino ├── ReadCSV └── ReadCSV.ino └── WriteCSV └── WriteCSV.ino /.gitignore: -------------------------------------------------------------------------------- 1 | CSVFileTest/.vs/CSVFileTest/v14/.suo 2 | CSVFileTest/__vm/ 3 | *.sln 4 | *.vcxproj* 5 | Release/ 6 | -------------------------------------------------------------------------------- /CSVFile.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSV File - extend class for SdFat library 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 18.07.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #include "CSVFile.h" 11 | 12 | // We use UNIX-style end of line 13 | #define CSV_END_OF_LINE '\n' 14 | #define NULL_CHAR '\0' 15 | 16 | #if CSV_FILE_ENABLE_DELETING_LINE || CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 17 | // *** Utilities *** 18 | bool CSVFile::isCurrentSubstring(const char * substr) 19 | { 20 | while(*substr) 21 | { 22 | int chVal = read(); 23 | if (*substr++ != chVal) 24 | { 25 | return false; 26 | } 27 | } 28 | return true; 29 | } 30 | #endif //CSV_FILE_ENABLE_DELETING_LINE || CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 31 | 32 | #define MAXIMAL_DIGITS_IN_UNSIGNED_INT 5 //Max:65 535 33 | #define ANSII_ZERO 0x30 34 | // Write number to file as text. Return false if failed. 35 | int CSVFile::writeNumber(unsigned int number) 36 | { 37 | unsigned int power10 = pow(10, MAXIMAL_DIGITS_IN_UNSIGNED_INT-1); //10000 38 | byte digits = 0; 39 | byte currentDigit = 0; 40 | // Without last from right digit 41 | while (power10 != 1) 42 | { 43 | currentDigit = number / power10; 44 | number = number % power10; 45 | power10 /= 10; 46 | 47 | if (digits == 0 && currentDigit == 0) 48 | continue; 49 | 50 | digits += 1; 51 | write(ANSII_ZERO + currentDigit); 52 | } 53 | 54 | // Last digit 55 | digits += 1; 56 | return write(ANSII_ZERO + number) == -1 ? -1 : digits; 57 | } 58 | #undef MAXIMAL_DIGITS_IN_UNSIGNED_INT 59 | #undef ANSII_ZERO 60 | 61 | // *** Files *** 62 | bool CSVFile::isEndOfFile() 63 | { 64 | return available() == 0; 65 | } 66 | 67 | // Always return true. 68 | // If first lines are delete then set 69 | // pointer on the begin of first not deleted 70 | // line or if all lines are deleted on the 71 | // end of file. 72 | bool CSVFile::gotoBeginOfFile() 73 | { 74 | rewind(); 75 | numField = 0; 76 | 77 | #if CSV_FILE_ENABLE_DELETING_LINE 78 | //Search for first not deleted line 79 | if (isLineMarkedAsDelete()) 80 | nextLine(); 81 | else 82 | gotoBeginOfLine(); 83 | #endif //CSV_FILE_ENABLE_DELETING_LINE 84 | 85 | numLine = 0; 86 | return true; 87 | } 88 | 89 | // *** Lines *** 90 | bool CSVFile::isBeginOfLine() 91 | { 92 | //Begin of file; 93 | if (!seekCur(-1)) 94 | { 95 | return true; 96 | } 97 | int ch = read(); 98 | return ch == CSV_END_OF_LINE; 99 | } 100 | 101 | bool CSVFile::isEndOfLine() 102 | { 103 | //End of file 104 | if (isEndOfFile()) 105 | { 106 | return true; 107 | } 108 | bool status_ = read() == CSV_END_OF_LINE; 109 | seekCur(-1); 110 | return status_; 111 | } 112 | 113 | //Return number current line. Lines are numbered from 0. 114 | unsigned int CSVFile::getNumberOfLine() 115 | { 116 | return numLine; 117 | } 118 | 119 | #if CSV_FILE_ENABLE_CHECK_EMPTY_LINE 120 | //After set pointer at end of line or at first not empty character 121 | //Require gotoBeginOfLine() 122 | bool CSVFile::isEmptyLine() 123 | { 124 | int chVal = read(); 125 | while (chVal >= 0) 126 | { 127 | if (((char) chVal) == CSV_END_OF_LINE) 128 | { 129 | seekCur(-1); 130 | return true; 131 | } 132 | else if (((char)chVal) != NULL_CHAR) 133 | { 134 | return false; 135 | } 136 | chVal = read(); 137 | } 138 | return true; //End of line 139 | } 140 | #endif 141 | 142 | #if CSV_FILE_ENABLE_DELETING_LINE 143 | //Require gotoBeginOfLine(); 144 | //After process DON'T set pointer at begin of line 145 | bool CSVFile::isLineMarkedAsDelete() 146 | { 147 | return isCurrentSubstring(CSV_FILE_DELETE_MARKER); 148 | } 149 | #endif //CSV_FILE_ENABLE_DELETING_LINE 150 | 151 | bool CSVFile::gotoBeginOfLine() 152 | { 153 | numField = 0; 154 | //Begin of file; 155 | if (!seekCur(-1)) 156 | { 157 | return true; 158 | } 159 | 160 | int chVal = read(); 161 | 162 | while (chVal != CSV_END_OF_LINE) 163 | { 164 | if (!seekCur(-2)) 165 | { 166 | seekCur(-1); 167 | return true; 168 | } 169 | chVal = read(); 170 | } 171 | 172 | return true; 173 | } 174 | 175 | bool CSVFile::nextLine() 176 | { 177 | int chVal = read(); 178 | //End of file 179 | if (chVal < 0) 180 | { 181 | return false; 182 | } 183 | //Content of current line 184 | while (chVal != CSV_END_OF_LINE) 185 | { 186 | chVal = read(); 187 | //End of file 188 | if (chVal < 0) 189 | { 190 | return false; 191 | } 192 | } 193 | //We are in next line 194 | #if CSV_FILE_ENABLE_DELETING_LINE 195 | //Ignored Deleted lines 196 | if (isLineMarkedAsDelete()) 197 | { 198 | return nextLine(); 199 | } 200 | else 201 | { 202 | gotoBeginOfLine(); 203 | } 204 | #else 205 | numField = 0; 206 | #endif //CSV_FILE_ENABLE_DELETING_LINE 207 | 208 | numLine += 1; 209 | return true; 210 | } 211 | 212 | bool CSVFile::gotoLine(unsigned int number) 213 | { 214 | if (number == numLine) 215 | { 216 | return gotoBeginOfLine(); 217 | } 218 | else if (number < numLine) 219 | { 220 | gotoBeginOfFile(); 221 | } 222 | 223 | while (number != numLine) 224 | { 225 | if (!nextLine()) 226 | { 227 | return false; 228 | } 229 | } 230 | return true; 231 | } 232 | 233 | #if CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 234 | // Find first line starts with argument string 235 | // from CURRENT position. 236 | // Probably you should call gotoBeginOfFile(). 237 | bool CSVFile::gotoLine(const char * startsWith_) 238 | { 239 | while (!isCurrentSubstring(startsWith_)) 240 | { 241 | if (!nextLine()) 242 | { 243 | return false; 244 | } 245 | } 246 | gotoBeginOfLine(); 247 | return true; 248 | } 249 | #endif 250 | 251 | #if CSV_FILE_ENABLE_DELETING_LINE 252 | //Required call gotoBeginOfLine 253 | //Don't set pointer at begin of line. 254 | //If you call isLineMarkedAsDelete after 255 | //this function you get false. 256 | //Line should be long then CSV_FILE_DELETE_MARKER 257 | bool CSVFile::markLineAsDelete() 258 | { 259 | #if CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 260 | //Check line size 261 | int chVal; 262 | for (byte i = 0; i < CSV_FILE_CSV_FILE_DELETE_MARKER_SIZE; ++i) 263 | { 264 | chVal = read(); 265 | if (chVal < 0 || ((char) chVal) == CSV_END_OF_LINE) 266 | { 267 | seekCur(-(i+1)); 268 | return false; 269 | } 270 | } 271 | 272 | seekCur(-CSV_FILE_CSV_FILE_DELETE_MARKER_SIZE); 273 | #endif //CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 274 | 275 | return write(CSV_FILE_DELETE_MARKER) != 0; 276 | } 277 | #endif //CSV_FILE_ENABLE_DELETING_LINE 278 | 279 | //Use only on the end of file 280 | bool CSVFile::addLine() 281 | { 282 | #if CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 283 | if (!isEndOfFile()) 284 | { 285 | return false; 286 | } 287 | #endif 288 | numLine += 1; 289 | numField = 0; 290 | write(CSV_END_OF_LINE); // Unix style end of line 291 | return true; 292 | } 293 | 294 | // *** Fields *** 295 | bool CSVFile::isEndOfField() 296 | { 297 | //End of line 298 | if (isEndOfLine()) 299 | { 300 | return true; 301 | } 302 | else 303 | { 304 | bool status_ = read() == CSV_FILE_DELIMITER; 305 | seekCur(-1); 306 | return status_; 307 | } 308 | } 309 | 310 | // Always return TRUE 311 | bool CSVFile::gotoBeginOfField() 312 | { 313 | //Begin of file; 314 | if (! seekCur(-1)) 315 | { 316 | return true; 317 | } 318 | 319 | int chVal = read(); 320 | 321 | while (chVal != CSV_END_OF_LINE && chVal != CSV_FILE_DELIMITER) 322 | { 323 | //Begin of file 324 | if (!seekCur(-2)) 325 | { 326 | seekCur(-1); 327 | return true; 328 | } 329 | chVal = read(); 330 | } 331 | 332 | return true; 333 | } 334 | 335 | bool CSVFile::gotoField(byte num) { 336 | if (num == numField) 337 | { 338 | gotoBeginOfField(); 339 | return true; 340 | } 341 | else if (num < numField) 342 | { 343 | gotoBeginOfLine(); 344 | } 345 | 346 | while (num != numField) 347 | { 348 | if (!nextField()) 349 | { 350 | return false; 351 | } 352 | } 353 | return true; 354 | } 355 | 356 | // Return number of reading bytes 357 | // Return 0 when end reading current field 358 | // Auto-safe. If reading bytes is less then @bufferSize 359 | // then after last reading bytes is put null char. 360 | byte CSVFile::readField(char * buffer_, byte bufferSize) 361 | { 362 | byte numReading = read(buffer_, bufferSize); 363 | char chVal = 0; 364 | byte correctBytes = 0; 365 | 366 | for (correctBytes=0; correctBytes < numReading; ++correctBytes) 367 | { 368 | chVal = buffer_[correctBytes]; 369 | if (chVal == CSV_END_OF_LINE || chVal == CSV_FILE_DELIMITER) 370 | { 371 | // If read over end of field or end of line then we should 372 | // seek before first delimiter or first end of line 373 | // -numReading is state before reading 374 | // correctBytes is number of bytes to first delimiter or first end of line 375 | // (without this char) 376 | // Sum -numReading and correctBytes is position before delimiter 377 | seekCur(-numReading + correctBytes); 378 | break; 379 | } 380 | } 381 | 382 | // If read file to end or if not read full single field 383 | // then position in file is correctBytes. 384 | 385 | // Special case: last reading bytes is first half of non-ASCII character 386 | if (correctBytes == numReading && chVal < 0 && buffer_[correctBytes - 2] > 0) //correctBytes - 1 is index of chVal 387 | { 388 | correctBytes -= 1; 389 | seekCur(-1); 390 | } 391 | 392 | if (correctBytes < bufferSize) 393 | buffer_[correctBytes] = NULL_CHAR; 394 | 395 | return correctBytes; 396 | } 397 | 398 | //Buffer size should be equals or greater then decimal place 399 | //reading value. 400 | //Return FALSE if not read field or field is empty. 401 | //Number can have a sign. 402 | //Number can be preceded by whitespaces. 403 | //Converting to number is end when is read first non-number char 404 | //Warning! If reading buffer don't contain any number char 405 | //then function return 0! 406 | bool CSVFile::readField(int& value, char * buffer_, byte bufferSize) 407 | { 408 | int reading = readField(buffer_, bufferSize); 409 | if (reading > 0) 410 | { 411 | value = atoi(buffer_); 412 | return true; 413 | } 414 | else 415 | { 416 | return false; 417 | } 418 | } 419 | 420 | // If this field is end field in line (file) 421 | // NOT go to next line and return FALSE 422 | // When return true set pointer to position 423 | // first char of file (after delimiter last field). 424 | bool CSVFile::nextField() 425 | { 426 | int chVal = read(); 427 | //End of file 428 | if (chVal == -1) 429 | { 430 | return false; 431 | } 432 | //Any content of field 433 | //Not end of file, not end of field, not end of line 434 | while (chVal >= 0 && chVal != CSV_FILE_DELIMITER && chVal != CSV_END_OF_LINE) 435 | { 436 | chVal = read(); 437 | } 438 | //End of line 439 | if (chVal == CSV_END_OF_LINE) 440 | { 441 | seekCur(-1); 442 | return false; 443 | } 444 | 445 | numField += 1; 446 | 447 | return true; 448 | } 449 | 450 | //I cannot resize field. 451 | //Required gotoBeginOfField() (pointer at begin of field) 452 | //Should be sure, that number of decimal place of value 453 | //is equal or less then field size. 454 | //Set pointer at the end of field. Before deilimiter or end of line 455 | bool CSVFile::editField(byte value) 456 | { 457 | if (writeNumber(value) == -1) 458 | return false; 459 | 460 | int ch = read(); 461 | //End of file, end of line, end of field 462 | while(ch >= 0 && ch != CSV_END_OF_LINE && ch != CSV_FILE_DELIMITER) 463 | { 464 | seekCur(-1); 465 | write((byte)NULL_CHAR); 466 | ch = read(); 467 | } 468 | 469 | if (ch >= 0) 470 | { 471 | seekCur(-1); 472 | } 473 | 474 | return true; 475 | } 476 | 477 | //Add only delimiter, without content. 478 | bool CSVFile::addField() 479 | { 480 | #if CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 481 | if (!isEndOfFile()) 482 | { 483 | return false; 484 | } 485 | #endif 486 | // On the begin of line this add empty field too 487 | write(CSV_FILE_DELIMITER); 488 | 489 | numField += 1; 490 | 491 | return true; 492 | } 493 | 494 | //Call this only on the end of file. 495 | bool CSVFile::addField(const char * content) 496 | { 497 | #if CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 498 | if (!isEndOfFile()) 499 | { 500 | return false; 501 | } 502 | #endif 503 | if (!isBeginOfLine()) 504 | { 505 | write(CSV_FILE_DELIMITER); 506 | } 507 | 508 | if (write(content) == -1) 509 | return false; 510 | 511 | numField += 1; 512 | 513 | return true; 514 | } 515 | 516 | //Method to add variable size number field. 517 | bool CSVFile::addField(unsigned int content) 518 | { 519 | #if CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 520 | if (!isEndOfFile()) 521 | { 522 | return false; 523 | } 524 | #endif 525 | if (!isBeginOfLine()) 526 | { 527 | write(CSV_FILE_DELIMITER); 528 | } 529 | 530 | if (writeNumber(content) == -1) 531 | return false; 532 | 533 | numField += 1; 534 | 535 | return true; 536 | } 537 | 538 | //Method for add fixed size number field. 539 | //Therefore support only bytes @fieldSize should be 540 | //equals or less then 3. 541 | bool CSVFile::addField(byte content, byte fieldSize) 542 | { 543 | #if CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 544 | if (!isEndOfFile()) 545 | { 546 | return false; 547 | } 548 | #endif 549 | if (!isBeginOfLine()) 550 | { 551 | write(CSV_FILE_DELIMITER); 552 | } 553 | 554 | fieldSize -= writeNumber(content); 555 | 556 | while (fieldSize > 0) 557 | { 558 | fieldSize -= write((byte)NULL_CHAR); 559 | } 560 | 561 | numField += 1; 562 | 563 | return true; 564 | } 565 | 566 | // *** Interact with file *** 567 | // Copy current field to other file. 568 | // Pointer in target file should be set at end of file 569 | byte CSVFile::copyField(FatFile * target) 570 | { 571 | byte copied = 0; 572 | int chVal = read(); 573 | while (chVal >= 0 && ((char)chVal != CSV_FILE_DELIMITER) && ((char)chVal != CSV_END_OF_LINE)) 574 | { 575 | target->write((char)chVal); 576 | copied += 1; 577 | chVal = read(); 578 | } 579 | 580 | if (chVal >= 0) // ((char)chVal) == CSV_END_OF_LINE || ((char) chVal) == CSV_FILE_DELIMITER) 581 | { 582 | seekCur(-1); 583 | } 584 | 585 | return copied; 586 | } 587 | 588 | #undef CSV_FILE_DELIMITER 589 | #undef CSV_END_OF_LINE 590 | #undef CSV_FILE_DELETE_MARKER 591 | #undef CSV_FILE_CSV_FILE_DELETE_MARKER_SIZE 592 | #undef NULL_CHAR 593 | #undef CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 594 | -------------------------------------------------------------------------------- /CSVFile.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSV File - extend class for SdFat library 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 18.07.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef CSVFile_h 11 | #define CSVFile_h 12 | 13 | #include 14 | #include "CSVFileConfig.h" 15 | 16 | class CSVFile : public FatFile { 17 | 18 | private: 19 | unsigned int numLine = 0; 20 | byte numField = 0; 21 | 22 | #if CSV_FILE_ENABLE_DELETING_LINE || CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 23 | // *** Utilities *** 24 | //UNSAFE FUNCTION! 25 | //Change pointer position 26 | //Don't stop when end of field/line. 27 | //May destroy correct line/field counting 28 | bool isCurrentSubstring(const char * substr); 29 | #endif //CSV_FILE_ENABLE_DELETING_LINE || CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 30 | 31 | // Write number to file as text. Return false if failed. 32 | int writeNumber(unsigned int number); 33 | 34 | public: 35 | CSVFile() : FatFile() {} 36 | virtual ~CSVFile() {} 37 | 38 | // *** Files *** 39 | bool isEndOfFile(); 40 | // Always return true. If first lines are delete then set 41 | // pointer on the begin of first not deleted line or if all lines are deleted on the 42 | // end of file. 43 | bool gotoBeginOfFile(); 44 | 45 | // *** Lines *** 46 | bool isBeginOfLine(); 47 | bool isEndOfLine(); 48 | 49 | //Return number current line. Lines are numbered from 0. 50 | unsigned int getNumberOfLine(); 51 | 52 | #if CSV_FILE_ENABLE_CHECK_EMPTY_LINE 53 | //After set pointer at end of line or at first not empty character 54 | //Require gotoBeginOfLine() 55 | bool isEmptyLine(); 56 | #endif //CSV_FILE_ENABLE_CHECK_EMPTY_LINE 57 | #if CSV_FILE_ENABLE_DELETING_LINE 58 | //Require gotoBeginOfLine();1 59 | bool isLineMarkedAsDelete(); 60 | #endif //CSV_FILE_ENABLE_DELETING_LINE 61 | bool gotoBeginOfLine(); 62 | 63 | // Return false when next line not exist. When return false 64 | // then positon in file is set at end. 65 | bool nextLine(); 66 | bool gotoLine(unsigned int number); 67 | 68 | #if CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 69 | // Find first line starts with argument string 70 | // from CURRENT position. 71 | // Probably you should call gotoBeginOfFile(). 72 | bool gotoLine(const char * startsWith_); 73 | #endif 74 | 75 | #if CSV_FILE_ENABLE_DELETING_LINE 76 | //Required call gotoBeginOfLine 77 | bool markLineAsDelete(); 78 | #endif //CSV_FILE_ENABLE_DELETING_LINE 79 | 80 | //Use only on the end of file 81 | bool addLine(); 82 | 83 | // *** Fields *** 84 | bool isEndOfField(); 85 | 86 | // Always return TRUE 87 | bool gotoBeginOfField(); 88 | bool gotoField(byte num); 89 | 90 | // Return number of reading bytes 91 | // Return 0 when end reading current field 92 | // Auto-safe. If reading bytes is less then @bufferSize 93 | // then after last reading bytes is put null char. 94 | byte readField(char * buffer_, byte bufferSize); 95 | 96 | //Buffer size should be equals or greater then decimal place 97 | //reading value. 98 | //Return FALSE if not read field or field is empty. 99 | //Number can have a sign. 100 | //Number can be preceded by whitespaces. 101 | //Converting to number is end when is read first non-number char 102 | //Warning! If reading buffer don't contain any number char 103 | //then function return 0! 104 | bool readField(int& value, char * buffer_, byte bufferSize); 105 | 106 | // If this field is end field in line (file) 107 | // NOT go to next line and return FALSE 108 | // When return true set pointer to position 109 | // first char of file (after delimiter last field). 110 | bool nextField(); 111 | 112 | //I cannot resize field. 113 | //Required gotoBeginOfField() (pointer at begin of field) 114 | //Should be sure, that number of decimal place of value 115 | //is equal or less then field size. 116 | //Set pointer at the end of field. Before deilimiter or end of line 117 | bool editField(byte value); 118 | 119 | //Call this only on the end of file 120 | //Add only delimiter, without content. 121 | bool addField(); 122 | 123 | //Call this only on the end of file. 124 | bool addField(const char * content); 125 | 126 | //Method to add variable size number field. 127 | bool addField(unsigned int content); 128 | 129 | //Method for add fixed size number field. 130 | //Therefore support only bytes @fieldSize should be 131 | //equals or less then 3. 132 | bool addField(byte content, byte fieldSize); 133 | 134 | // *** Interact with other file *** 135 | // Copy current field to other file. 136 | // Pointer in target file should be set at end of file 137 | byte copyField(FatFile * target); 138 | }; 139 | 140 | #endif //CSVFile_h 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /CSVFileConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef CSVFileConfig_h 2 | #define CSVFileConfig_h 3 | 4 | #define CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 0 5 | #define CSV_FILE_ENABLE_CHECK_EMPTY_LINE 0 6 | #define CSV_FILE_ENABLE_DELETING_LINE 1 7 | // If CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR is equals 1 adding function return false when 8 | // current operation can overwrite other data 9 | #define CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 0 10 | 11 | #define CSV_FILE_DELIMITER ';' 12 | 13 | #define CSV_FILE_CSV_FILE_DELETE_MARKER_SIZE 4 14 | #define CSV_FILE_DELETE_MARKER "@DEL" 15 | 16 | #endif //CSVFileConfig_h -------------------------------------------------------------------------------- /CSVFileTest/CSVFileTest.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | /* 15 | * The test files are very big 16 | * and only in part it can be stored 17 | * in flash memory. 18 | */ 19 | 20 | #include "CSVFileTestPart1.h" 21 | //#include "CSVFileTestPart2.h" 22 | //#include "CSVFileTestPart3.h" 23 | //#include "CSVFileTestPart4.h" 24 | //#include "CSVFileTestPart5.h" 25 | //#include "CSVFileTestPart6.h" 26 | 27 | void setup() { 28 | setupPinout(); 29 | SPI.begin(); 30 | 31 | #if ! SPI_USE_TRANSACTION 32 | SPI.setDataMode(0); 33 | SPI.setBitOrder(MSBFIRST); 34 | #endif //SPI_USE_TRANSACTION 35 | 36 | Serial.begin(9600); 37 | 38 | setupTest(); 39 | } 40 | 41 | void loop() { 42 | Test::run(); 43 | } 44 | -------------------------------------------------------------------------------- /CSVFileTest/CSVFileTestInit.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef CSVFileTestInit_h 11 | #define CSVFileTestInit_h 12 | 13 | #include 14 | #include 15 | #include 16 | #include "Config.h" 17 | 18 | #define UNIT_TEST_FILE "unit.txt" 19 | #define SECOND_UNIT_TEST_FILE "unit2.txt" 20 | 21 | SdFat sd; 22 | CSVFile file; 23 | 24 | const int length1Line1Field = 3; 25 | const int length1Line2Field = 2; 26 | const int length1Line3Field = 4; 27 | const int length2Line1Field = 1; 28 | const int length2Line2Field = 3; 29 | const int length2Line3Field = 2; 30 | const int length3Line1Field = 3; 31 | const int length3Line2Field = 3; 32 | const int length3Line3Field = 4 + 1; //Polish diacritic 33 | const int length5Line1Field = 8; 34 | const int length5Line2Field = 0; 35 | const int length5Line3Field = 0; 36 | 37 | const int lengthDelimiter = 1; 38 | const int lengthEndOfLine = 1; 39 | 40 | const int length1Line = length1Line1Field + lengthDelimiter + length1Line2Field + lengthDelimiter + length1Line3Field + lengthEndOfLine; 41 | const int length2Line = length2Line1Field + lengthDelimiter + length2Line2Field + lengthDelimiter + length2Line3Field + lengthEndOfLine; 42 | const int length3Line = length3Line1Field + lengthDelimiter + length3Line2Field + lengthDelimiter + length3Line3Field + lengthEndOfLine; 43 | const int length4Line = 27; 44 | const int length5Line = length5Line1Field + lengthDelimiter + length5Line2Field + lengthDelimiter + length5Line3Field; 45 | 46 | const int start1Line = 0; 47 | const int start2Line = start1Line + length1Line; 48 | const int start3Line = start2Line + length2Line; 49 | const int start4Line = start3Line + length3Line; 50 | const int start5Line = start4Line + length4Line; 51 | 52 | void setupTest() { 53 | if (!sd.begin(PIN_SD_CS, SPI_SD_MAX_SPEED)) { 54 | Serial.println("SD card init error"); 55 | return; 56 | } 57 | if (sd.exists(UNIT_TEST_FILE) && !sd.remove(UNIT_TEST_FILE)) 58 | { 59 | Serial.println("Failed init remove file"); 60 | return; 61 | } 62 | } 63 | 64 | void endTest() { 65 | file.remove(); 66 | file.close(); 67 | } 68 | 69 | void beginTest() { 70 | if (file.isOpen() || sd.exists(UNIT_TEST_FILE)) 71 | endTest(); 72 | 73 | if (!file.open(UNIT_TEST_FILE, O_RDWR | O_CREAT)) { 74 | Serial.println("Failed open file"); 75 | } 76 | file.write("Ala;ma;kota\n"); 77 | file.write("2;kot;33\n"); 78 | file.write("Ale;Oto;cała\n"); 79 | file.write("@DELcostam;skasowana;linia\n"); 80 | file.write("historia;;"); 81 | file.gotoBeginOfFile(); 82 | } 83 | 84 | #endif //CSVFileTestInit_h 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /CSVFileTest/CSVFileTestPart1.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef CSVFileTestPart_1_h 11 | #define CSVFileTestPart_1_h 12 | 13 | #include "CSVFileTestInit.h" 14 | 15 | test(gotoBeginOfFile_ok) { 16 | beginTest(); 17 | assertTrue(file.gotoBeginOfFile()); 18 | assertEqual(file.curPosition(), 0); 19 | file.seekSet(10); 20 | assertTrue(file.gotoBeginOfFile()); 21 | assertEqual(file.curPosition(), 0); 22 | file.seekSet(8); 23 | assertTrue(file.gotoBeginOfFile()); 24 | assertEqual(file.curPosition(), 0); 25 | file.gotoField(0); 26 | assertEqual(file.curPosition(), 0); 27 | endTest(); 28 | } 29 | 30 | //test(substring_ok) { 31 | // beginTest(); 32 | // bool status_ = file.isCurrentSubstring("Ala"); 33 | // assertTrue(status_); 34 | // file.gotoBeginOfFile(); 35 | // status_ = file.isCurrentSubstring("A"); 36 | // assertTrue(status_); 37 | // file.gotoBeginOfFile(); 38 | // status_ = file.isCurrentSubstring("Al"); 39 | // assertTrue(status_); 40 | // file.gotoBeginOfFile(); 41 | // status_ = file.isCurrentSubstring(""); 42 | // assertTrue(status_); 43 | // endTest(); 44 | //} 45 | // 46 | //test(substring_failed) { 47 | // beginTest(); 48 | // file.gotoBeginOfFile(); 49 | // bool status_ = file.isCurrentSubstring("ala"); 50 | // assertFalse(status_); 51 | // file.gotoBeginOfFile(); 52 | // status_ = file.isCurrentSubstring("ba"); 53 | // assertFalse(status_); 54 | // file.gotoBeginOfFile(); 55 | // status_ = file.isCurrentSubstring("123"); 56 | // assertFalse(status_); 57 | // file.gotoBeginOfFile(); 58 | // status_ = file.isCurrentSubstring("!@"); 59 | // assertFalse(status_); 60 | // endTest(); 61 | //} 62 | 63 | test(isEndOfFile_ok) { 64 | beginTest(); 65 | file.seekEnd(); 66 | assertTrue(file.isEndOfFile()); 67 | endTest(); 68 | } 69 | 70 | test(isEndOfFile_failed) { 71 | beginTest(); 72 | assertFalse(file.isEndOfFile()); 73 | endTest(); 74 | } 75 | 76 | test(isBeginOfLine_ok) { 77 | beginTest(); 78 | assertTrue(file.isBeginOfLine()); 79 | endTest(); 80 | } 81 | 82 | test(isBeginOfLine_ok_2) { 83 | beginTest(); 84 | file.nextLine(); 85 | assertTrue(file.isBeginOfLine()); 86 | endTest(); 87 | } 88 | 89 | test(isBeginOfLine_failed) { 90 | beginTest(); 91 | file.seekSet(1); 92 | assertFalse(file.isBeginOfLine()); 93 | endTest(); 94 | } 95 | 96 | test(isEndOfLine_ok) { 97 | beginTest(); 98 | file.nextLine(); 99 | file.seekCur(-1); 100 | assertTrue(file.isEndOfLine()); 101 | endTest(); 102 | } 103 | 104 | test(isEndOfLine_ok_2) { 105 | beginTest(); 106 | file.seekEnd(); 107 | assertTrue(file.isEndOfLine()); 108 | endTest(); 109 | } 110 | 111 | test(isEndOfLine_failed) { 112 | beginTest(); 113 | file.seekCur(1); 114 | assertFalse(file.isEndOfLine()); 115 | endTest(); 116 | } 117 | 118 | test(isLineMarkedAsDelete_ok) { 119 | beginTest(); 120 | assertFalse(file.isLineMarkedAsDelete()); 121 | endTest(); 122 | } 123 | 124 | test(isLineMarkedAsDelete_ok2) { 125 | beginTest(); 126 | file.nextLine(); 127 | file.nextLine(); 128 | file.seekCur(length3Line); 129 | assertTrue(file.isLineMarkedAsDelete()); 130 | endTest(); 131 | } 132 | 133 | test(isLineMarkedAsDelete_failed) { 134 | beginTest(); 135 | file.nextLine(); 136 | file.nextLine(); 137 | file.seekCur(length3Line); 138 | assertTrue(file.isLineMarkedAsDelete()); 139 | // When we repeat check at the same line we get false, 140 | // because this function don't back pointer in file 141 | assertFalse(file.isLineMarkedAsDelete()); 142 | endTest(); 143 | } 144 | 145 | test(gotoBeginOfLine_ok) { 146 | beginTest(); 147 | assertTrue(file.gotoBeginOfLine()); 148 | assertTrue(file.isBeginOfLine()); 149 | file.gotoField(0); 150 | assertTrue(file.isBeginOfLine()); 151 | endTest(); 152 | } 153 | 154 | test(gotoBeginOfLine_ok2) { 155 | beginTest(); 156 | file.nextLine(); 157 | file.seekCur(2); 158 | assertTrue(file.gotoBeginOfLine()); 159 | assertTrue(file.isBeginOfLine()); 160 | file.gotoField(0); 161 | assertTrue(file.isBeginOfLine()); 162 | endTest(); 163 | } 164 | 165 | test(nextLine_ok) { 166 | beginTest(); 167 | assertTrue(file.nextLine()); //Second line 168 | assertTrue(file.isBeginOfLine()); 169 | assertEqual(file.curPosition(), start2Line); 170 | assertTrue(file.nextLine()); //Third line 171 | assertTrue(file.isBeginOfLine()); 172 | assertEqual(file.curPosition(), start3Line); 173 | file.gotoField(0); 174 | assertTrue(file.isBeginOfLine()); 175 | //Delete line 176 | assertTrue(file.nextLine()); //Fourth line 177 | assertTrue(file.isBeginOfLine()); 178 | assertEqual(file.curPosition(), start5Line); 179 | file.gotoField(0); 180 | assertTrue(file.isBeginOfLine()); 181 | endTest(); 182 | } 183 | 184 | test(nextLine_failed) { 185 | beginTest(); 186 | file.seekEnd(); 187 | uint32_t endFilePosition = file.curPosition(); 188 | file.seekEnd(); 189 | assertFalse(file.nextLine()); 190 | assertEqual(file.curPosition(), endFilePosition); 191 | file.seekEnd(-3); 192 | assertFalse(file.nextLine()); 193 | assertEqual(file.curPosition(), endFilePosition); 194 | } 195 | 196 | #endif //CSVFileTestPart_1_h 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /CSVFileTest/CSVFileTestPart2.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef CSVFileTestPart_2_h 11 | #define CSVFileTestPart_2_h 12 | 13 | #include "CSVFileTestInit.h" 14 | 15 | test(gotoLine_num_ok) { 16 | beginTest(); 17 | // Enumeration from 0 to 3 18 | assertTrue(file.gotoLine(0)); //Second line 19 | assertTrue(file.isBeginOfLine()); 20 | assertEqual(file.curPosition(), 0); 21 | assertTrue(file.gotoLine(1)); //Second line 22 | assertTrue(file.isBeginOfLine()); 23 | assertEqual(file.curPosition(), start2Line); 24 | assertTrue(file.gotoLine(2)); //Third line 25 | assertTrue(file.isBeginOfLine()); 26 | assertEqual(file.curPosition(), start3Line); 27 | //Delete line 28 | assertTrue(file.gotoLine(3)); //Fourth line 29 | assertTrue(file.isBeginOfLine()); 30 | assertEqual(file.curPosition(), start5Line); 31 | 32 | // Enumeration from 3 to 0 33 | file.seekEnd(); 34 | assertTrue(file.gotoLine(3)); //Fourth line 35 | assertTrue(file.isBeginOfLine()); 36 | assertEqual(file.curPosition(), start5Line); 37 | assertTrue(file.gotoLine(2)); //Third line 38 | assertTrue(file.isBeginOfLine()); 39 | assertEqual(file.curPosition(), start3Line); 40 | assertTrue(file.gotoLine(1)); //Second line 41 | assertTrue(file.isBeginOfLine()); 42 | assertEqual(file.curPosition(), start2Line); 43 | assertTrue(file.gotoLine(0)); //Second line 44 | assertTrue(file.isBeginOfLine()); 45 | assertEqual(file.curPosition(), 0); 46 | 47 | // Random access 48 | assertTrue(file.gotoLine(2)); //Third line 49 | assertTrue(file.isBeginOfLine()); 50 | assertEqual(file.curPosition(), start3Line); 51 | assertTrue(file.gotoLine(0)); //Second line 52 | assertTrue(file.isBeginOfLine()); 53 | assertEqual(file.curPosition(), 0); 54 | assertTrue(file.gotoLine(3)); //Fourth line 55 | assertTrue(file.isBeginOfLine()); 56 | assertEqual(file.curPosition(), start5Line); 57 | assertTrue(file.gotoLine(1)); //Second line 58 | assertTrue(file.isBeginOfLine()); 59 | assertEqual(file.curPosition(), start2Line); 60 | 61 | endTest(); 62 | } 63 | 64 | test(gotoLine_num_failed) { 65 | beginTest(); 66 | assertFalse(file.gotoLine(18)); 67 | file.seekEnd(); 68 | int endOfFile = file.curPosition(); 69 | file.gotoBeginOfLine(); 70 | assertFalse(file.gotoLine(-17)); 71 | assertEqual(file.curPosition(), endOfFile); 72 | endTest(); 73 | } 74 | 75 | #endif //CSVFileTestPart_2_h 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /CSVFileTest/CSVFileTestPart3.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef CSVFileTestPart_3_h 11 | #define CSVFileTestPart_3_h 12 | 13 | #include "CSVFileTestInit.h" 14 | 15 | test(gotoLine_start_ok) { 16 | #if CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 17 | beginTest(); 18 | // Enumeration from 0 to 3 19 | 20 | assertTrue(file.gotoLine("Ala")); //Second line 21 | assertTrue(file.isBeginOfLine()); 22 | assertEqual(file.curPosition(), 0); 23 | assertTrue(file.gotoLine("2")); //Second line 24 | assertTrue(file.isBeginOfLine()); 25 | assertEqual(file.curPosition(), start2Line); 26 | assertTrue(file.gotoLine("Ale")); //Third line 27 | assertTrue(file.isBeginOfLine()); 28 | assertEqual(file.curPosition(), start3Line); 29 | //Delete line 30 | assertTrue(file.gotoLine("historia")); //Fourth line 31 | assertTrue(file.isBeginOfLine()); 32 | assertEqual(file.curPosition(), start5Line); 33 | 34 | // Enumeration from 3 to 0 35 | file.seekEnd(); 36 | assertFalse(file.gotoLine("his")); //Fourth line 37 | file.gotoBeginOfFile(); 38 | assertTrue(file.gotoLine("his")); //Fourth line 39 | assertTrue(file.isBeginOfLine()); 40 | assertEqual(file.curPosition(), start5Line); 41 | assertFalse(file.gotoLine("Ale;")); //Third line 42 | file.gotoBeginOfFile(); 43 | assertTrue(file.gotoLine("Ale;")); //Third line 44 | assertTrue(file.isBeginOfLine()); 45 | assertEqual(file.curPosition(), start3Line); 46 | assertFalse(file.gotoLine("2;kot")); //Second line 47 | file.gotoBeginOfFile(); 48 | assertTrue(file.gotoLine("2;kot")); //Second line 49 | assertTrue(file.isBeginOfLine()); 50 | assertEqual(file.curPosition(), start2Line); 51 | 52 | // Random access 53 | assertTrue(file.gotoLine("Ale")); //Third line 54 | assertTrue(file.isBeginOfLine()); 55 | assertEqual(file.curPosition(), start3Line); 56 | file.gotoBeginOfFile(); 57 | assertTrue(file.gotoLine("Ala;")); //Second line 58 | assertTrue(file.isBeginOfLine()); 59 | assertEqual(file.curPosition(), 0); 60 | assertTrue(file.gotoLine("hi")); //Fourth line 61 | assertTrue(file.isBeginOfLine()); 62 | assertEqual(file.curPosition(), start5Line); 63 | file.gotoBeginOfFile(); 64 | assertTrue(file.gotoLine("2;")); //Second line 65 | assertTrue(file.isBeginOfLine()); 66 | assertEqual(file.curPosition(), start2Line); 67 | 68 | endTest(); 69 | #else 70 | skip(); 71 | return; 72 | #endif //CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 73 | } 74 | 75 | test(gotoLine_start_failed) { 76 | #if CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 77 | beginTest(); 78 | assertFalse(file.gotoLine("ala")); 79 | assertTrue(file.isEndOfFile()); 80 | assertFalse(file.gotoLine("xxx")); 81 | assertTrue(file.isEndOfFile()); 82 | endTest(); 83 | #else 84 | skip(); 85 | return; 86 | #endif //CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 87 | } 88 | 89 | test(markLineAsDelete_begin_file_ok) { 90 | beginTest(); 91 | file.gotoBeginOfFile(); 92 | assertTrue(file.markLineAsDelete()); 93 | file.gotoBeginOfFile(); 94 | assertFalse(file.isLineMarkedAsDelete()); 95 | file.seekSet(0); 96 | assertTrue(file.isLineMarkedAsDelete()); 97 | endTest(); 98 | } 99 | 100 | test(markLineAsDelete_remark_ok) { 101 | beginTest(); 102 | // Mark first 103 | file.gotoBeginOfFile(); 104 | assertTrue(file.markLineAsDelete()); 105 | file.gotoBeginOfFile(); 106 | assertFalse(file.isLineMarkedAsDelete()); 107 | file.seekSet(0); 108 | assertTrue(file.isLineMarkedAsDelete()); 109 | // Mark second 110 | file.gotoBeginOfFile(); 111 | assertTrue(file.markLineAsDelete()); 112 | file.gotoBeginOfFile(); 113 | assertFalse(file.isLineMarkedAsDelete()); 114 | file.seekSet(start2Line); 115 | assertTrue(file.isLineMarkedAsDelete()); 116 | endTest(); 117 | } 118 | 119 | test(markLineAsDelete_ok) { 120 | beginTest(); 121 | file.nextLine(); 122 | assertTrue(file.markLineAsDelete()); 123 | file.seekSet(start2Line); 124 | assertTrue(file.isLineMarkedAsDelete()); 125 | endTest(); 126 | } 127 | 128 | test(addLine_ok) { 129 | beginTest(); 130 | file.seekEnd(); 131 | assertTrue(file.addLine()); 132 | assertTrue(file.isEndOfFile()); 133 | file.seekCur(-1); 134 | assertTrue(file.read() == '\n'); 135 | endTest(); 136 | } 137 | 138 | test(addLine_failed) { 139 | #if ! CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 140 | skip(); 141 | return; 142 | #endif 143 | beginTest(); 144 | file.gotoBeginOfFile(); 145 | assertFalse(file.addLine()); 146 | endTest(); 147 | } 148 | #endif //CSVFileTestPart_3_h 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /CSVFileTest/CSVFileTestPart4.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef CSVFileTestPart_4_h 11 | #define CSVFileTestPart_4_h 12 | 13 | #include "CSVFileTestInit.h" 14 | #include "UnitTestUtils.h" 15 | 16 | test(isEndOfField_ok) { 17 | beginTest(); 18 | // End of first field second line 19 | file.gotoLine(1); 20 | file.seekCur(length2Line1Field); 21 | assertTrue(file.isEndOfField()); 22 | // End of last field second line 23 | file.gotoLine(2); 24 | file.seekCur(-1); 25 | assertTrue(file.isEndOfField()); 26 | // End of last field last line 27 | file.seekEnd(); 28 | assertTrue(file.isEndOfField()); 29 | // Second (empty field) last line 30 | file.seekEnd(-1); 31 | assertTrue(file.isEndOfField()); 32 | endTest(); 33 | } 34 | 35 | test(isEndOfField_failed) { 36 | beginTest(); 37 | // Begin of first field second line 38 | file.gotoLine(1); 39 | assertFalse(file.isEndOfField()); 40 | // Begin of second field third line 41 | file.gotoLine(2); 42 | file.seekCur(length3Line1Field); 43 | file.seekCur(1); 44 | assertFalse(file.isEndOfField()); 45 | // End of line - 1 46 | file.gotoLine(2); 47 | file.seekCur(-2); 48 | assertFalse(file.isEndOfField()); 49 | endTest(); 50 | } 51 | 52 | test(gotoBeginOfField_ok) { 53 | beginTest(); 54 | 55 | file.gotoBeginOfFile(); 56 | assertTrue(file.gotoBeginOfField()); 57 | assertEqual(file.curPosition(), 0); 58 | 59 | file.seekSet(start1Line + 2); 60 | assertTrue(file.gotoBeginOfField()); 61 | assertEqual(file.curPosition(), 0); 62 | 63 | file.seekSet(start1Line + length1Line1Field + lengthDelimiter + 1); 64 | assertTrue(file.gotoBeginOfField()); 65 | assertEqual(file.curPosition(), start1Line + length1Line1Field + lengthDelimiter); 66 | 67 | file.seekSet(start1Line + length1Line1Field + lengthDelimiter); 68 | assertTrue(file.gotoBeginOfField()); 69 | assertEqual(file.curPosition(), start1Line + length1Line1Field + lengthDelimiter); 70 | 71 | file.seekSet(start2Line + length2Line1Field + lengthDelimiter + length2Line2Field + lengthDelimiter + 1); 72 | assertTrue(file.gotoBeginOfField()); 73 | assertEqual(file.curPosition(), start2Line + length2Line1Field + lengthDelimiter + length2Line2Field + lengthDelimiter); 74 | 75 | // 3 field - empty 76 | file.seekSet(start5Line + length5Line1Field + lengthDelimiter + length5Line2Field + lengthDelimiter); // 5 line 3 field. 77 | assertTrue(file.gotoBeginOfField()); 78 | assertEqual(file.curPosition(), start5Line + length5Line1Field + lengthDelimiter + length5Line2Field + lengthDelimiter); 79 | 80 | endTest(); 81 | } 82 | 83 | test(readField_ok_whole_words) { 84 | beginTest(); 85 | char buffer_[4]; 86 | byte bufferSize = 4; 87 | 88 | file.gotoBeginOfFile(); 89 | 90 | assertEqual(file.readField(buffer_, bufferSize), length1Line1Field); 91 | assertTrue(equalCharArrays(buffer_, "Ala")); 92 | assertEqual(file.readField(buffer_, bufferSize), 0); 93 | 94 | file.nextField(); 95 | 96 | assertEqual(file.readField(buffer_, bufferSize), length1Line2Field); 97 | assertTrue(equalCharArrays(buffer_, "ma")); 98 | assertEqual(file.readField(buffer_, bufferSize), 0); 99 | 100 | file.nextField(); 101 | 102 | assertEqual(file.readField(buffer_, bufferSize), length1Line3Field); 103 | assertTrue(equalCharArrays(buffer_, "kota")); 104 | assertEqual(file.readField(buffer_, bufferSize), 0); 105 | 106 | endTest(); 107 | } 108 | 109 | test(readField_ok_part_words) { 110 | beginTest(); 111 | char buffer_[2]; 112 | byte bufferSize = 2; 113 | 114 | file.gotoBeginOfFile(); 115 | 116 | assertEqual(file.readField(buffer_, bufferSize), 2); 117 | assertTrue(equalCharArrays(buffer_, "Al")); 118 | assertEqual(file.readField(buffer_, bufferSize), 1); 119 | assertTrue(equalCharArrays(buffer_, "a")); 120 | 121 | file.nextField(); 122 | 123 | assertEqual(file.readField(buffer_, bufferSize), 2); 124 | assertTrue(equalCharArrays(buffer_, "ma")); 125 | 126 | file.nextField(); 127 | 128 | assertEqual(file.readField(buffer_, bufferSize), 2); 129 | assertTrue(equalCharArrays(buffer_, "ko")); 130 | assertEqual(file.readField(buffer_, bufferSize), 2); 131 | assertTrue(equalCharArrays(buffer_, "ta")); 132 | 133 | endTest(); 134 | } 135 | 136 | test(readField_ok_first_part_words) { 137 | beginTest(); 138 | char buffer_[2]; 139 | byte bufferSize = 2; 140 | 141 | file.gotoBeginOfFile(); 142 | 143 | assertEqual(file.readField(buffer_, bufferSize), 2); 144 | assertTrue(equalCharArrays(buffer_, "Al")); 145 | 146 | file.nextField(); 147 | 148 | file.nextField(); 149 | 150 | assertEqual(file.readField(buffer_, bufferSize), 2); 151 | assertTrue(equalCharArrays(buffer_, "ko")); 152 | 153 | endTest(); 154 | } 155 | 156 | test(read_ok_end_of_file) { 157 | beginTest(); 158 | char buffer_[2]; 159 | byte bufferSize = 2; 160 | 161 | file.seekEnd(); 162 | assertEqual(file.readField(buffer_, bufferSize), 0); 163 | 164 | endTest(); 165 | } 166 | 167 | test(read_num_ok) { 168 | beginTest(); 169 | 170 | int result = 0; 171 | char buffer_[2]; 172 | byte bufferSize = 2; 173 | 174 | file.gotoLine(1); 175 | assertTrue(file.readField(result, buffer_, bufferSize)); 176 | assertEqual(result, 2); 177 | 178 | file.nextField(); 179 | assertTrue(file.readField(result, buffer_, bufferSize)); 180 | assertEqual(result, 0); 181 | 182 | file.nextField(); 183 | assertTrue(file.readField(result, buffer_, bufferSize)); 184 | assertEqual(result, 33); 185 | 186 | endTest(); 187 | } 188 | 189 | test(read_num_failed) { 190 | beginTest(); 191 | 192 | int result = 0; 193 | char buffer_[2]; 194 | byte bufferSize = 2; 195 | 196 | file.gotoLine(4); 197 | //Empty field 198 | file.nextField(); 199 | assertFalse(file.readField(result, buffer_, bufferSize)); 200 | //End of file 201 | file.seekEnd(); 202 | assertFalse(file.readField(result, buffer_, bufferSize)); 203 | 204 | endTest(); 205 | } 206 | 207 | #endif //CSVFileTestPart_4_h 208 | 209 | -------------------------------------------------------------------------------- /CSVFileTest/CSVFileTestPart5.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef CSVFileTestPart5_h 11 | #define CSVFileTestPart5_h 12 | 13 | #include "CSVFileTestInit.h" 14 | #include "UnitTestUtils.h" 15 | 16 | test(nextField_ok) { 17 | beginTest(); 18 | file.gotoBeginOfFile(); 19 | assertTrue(file.nextField()); 20 | assertEqual(file.curPosition(), start1Line + length1Line1Field + lengthDelimiter); 21 | assertTrue(file.nextField()); 22 | assertEqual(file.curPosition(), start1Line + length1Line1Field + lengthDelimiter + length1Line2Field + lengthDelimiter); 23 | 24 | file.nextLine(); 25 | file.seekCur(1); 26 | assertTrue(file.nextField()); 27 | assertEqual(file.curPosition(), start2Line + length2Line1Field + lengthDelimiter); 28 | file.seekCur(2); 29 | assertTrue(file.nextField()); 30 | assertEqual(file.curPosition(), start2Line + length2Line1Field + lengthDelimiter + length2Line2Field + lengthDelimiter); 31 | endTest(); 32 | } 33 | 34 | test(nextField_failed) { 35 | beginTest(); 36 | 37 | file.gotoBeginOfFile(); 38 | file.nextField(); 39 | file.nextField(); 40 | assertFalse(file.nextField()); 41 | 42 | file.seekEnd(); 43 | assertFalse(file.nextField()); 44 | endTest(); 45 | } 46 | 47 | test(editField_ok) { 48 | beginTest(); 49 | 50 | char buffer_[3]; 51 | byte bufferSize = 3; 52 | 53 | file.gotoBeginOfFile(); 54 | file.editField(32); 55 | file.gotoBeginOfFile();; 56 | 57 | assertEqual(file.readField(buffer_, bufferSize), 3); 58 | assertTrue(equalCharArrays(buffer_, "32\0")); 59 | 60 | file.gotoBeginOfFile(); 61 | assertTrue(file.editField(145)); 62 | file.gotoBeginOfFile();; 63 | 64 | assertEqual(file.readField(buffer_, bufferSize), 3); 65 | assertTrue(equalCharArrays(buffer_, "145")); 66 | 67 | endTest(); 68 | } 69 | 70 | test(addField_ok_existed_row) { 71 | beginTest(); 72 | file.seekEnd(); 73 | 74 | assertTrue(file.addField("new_field")); 75 | assertTrue(file.gotoBeginOfLine()); // 0 field 76 | assertTrue(file.nextField()); // 1 field 77 | assertTrue(file.nextField()); // 2 field 78 | assertTrue(file.nextField()); // 3 (new) field 79 | 80 | char buffer_[9]; 81 | byte bufferSize = 9; 82 | 83 | assertEqual(file.readField(buffer_, bufferSize), 9); 84 | assertTrue(equalCharArrays(buffer_, "new_field")); 85 | 86 | assertTrue(file.gotoBeginOfLine()); // 0 field 87 | assertTrue(file.gotoField(3)); 88 | 89 | assertEqual(file.readField(buffer_, bufferSize), 9); 90 | assertTrue(equalCharArrays(buffer_, "new_field")); 91 | 92 | // Is end of file (without end delimiter or end line char)? 93 | assertEqual(file.available(), 0); 94 | endTest(); 95 | } 96 | 97 | test(addField_ok_new_line) { 98 | beginTest(); 99 | 100 | file.seekEnd(); 101 | file.addLine(); 102 | 103 | assertTrue(file.addField("new_field")); 104 | assertTrue(file.gotoBeginOfLine()); // 1 (new) field 105 | 106 | char buffer_[9]; 107 | byte bufferSize = 9; 108 | 109 | assertEqual(file.readField(buffer_, bufferSize), 9); 110 | assertTrue(equalCharArrays(buffer_, "new_field")); 111 | 112 | file.gotoField(0); 113 | assertEqual(file.readField(buffer_, bufferSize), 9); 114 | assertTrue(equalCharArrays(buffer_, "new_field")); 115 | // Is end of file (without end delimiter or end line char)? 116 | assertEqual(file.available(), 0); 117 | endTest(); 118 | } 119 | 120 | 121 | test(addField_failed) { 122 | #if ! CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 123 | skip(); 124 | return; 125 | #else 126 | beginTest(); 127 | 128 | file.gotoBeginOfFile(); 129 | assertFalse(file.addField("any")); 130 | endTest(); 131 | #endif //CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 132 | } 133 | 134 | test(addField_num_ok) { 135 | beginTest(); 136 | 137 | char buffer_[4]; 138 | byte bufferSize = 4; 139 | 140 | file.seekEnd(); 141 | assertTrue(file.addField(33, 2)); 142 | assertTrue(file.addField(7, 4)); 143 | assertTrue(file.gotoBeginOfLine()); // 0 field 144 | assertTrue(file.nextField()); // 1 field 145 | assertTrue(file.nextField()); // 2 field 146 | assertTrue(file.nextField()); // 3 (new) field 147 | 148 | assertEqual(file.readField(buffer_, bufferSize), 2); 149 | assertTrue(equalCharArrays(buffer_, "33")); 150 | 151 | assertTrue(file.nextField()); // 4 (new) field 152 | assertEqual(file.readField(buffer_, bufferSize), 4); 153 | 154 | Serial.print(F("Buffer: ")); 155 | Serial.println(buffer_); 156 | assertTrue(equalCharArrays(buffer_, "7\0\0\0")); 157 | 158 | assertTrue(file.gotoBeginOfLine()); // 0 field 159 | assertTrue(file.gotoField(3)); // 3 (new) field 160 | 161 | assertEqual(file.readField(buffer_, bufferSize), 2); 162 | assertTrue(equalCharArrays(buffer_, "33")); 163 | 164 | assertTrue(file.gotoField(4)); // 4 (new) field 165 | assertEqual(file.readField(buffer_, bufferSize), 4); 166 | assertTrue(equalCharArrays(buffer_, "7\0\0\0")); 167 | 168 | // Is end of file (without end delimiter or end line char)? 169 | assertEqual(file.available(), 0); 170 | endTest(); 171 | } 172 | #endif //CSVFileTestPart5_h 173 | 174 | -------------------------------------------------------------------------------- /CSVFileTest/CSVFileTestPart6.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef CSVFileTestPart6_h 11 | #define CSVFileTestPart6_h 12 | 13 | #include "CSVFileTestInit.h" 14 | #include "UnitTestUtils.h" 15 | 16 | test(getNumberOfLine_ok) 17 | { 18 | beginTest(); 19 | 20 | file.gotoBeginOfFile(); 21 | file.nextField(); 22 | assertEqual(file.getNumberOfLine(), 0); 23 | file.nextLine(); 24 | assertEqual(file.getNumberOfLine(), 1); 25 | file.nextLine(); 26 | file.nextField(); 27 | assertEqual(file.getNumberOfLine(), 2); 28 | assertEqual(file.getNumberOfLine(), 2); 29 | assertEqual(file.getNumberOfLine(), 2); 30 | file.nextLine(); //Skipped remove line 31 | assertEqual(file.getNumberOfLine(), 3); 32 | endTest(); 33 | } 34 | 35 | test(isEmptyLine_ok) 36 | { 37 | #if CSV_FILE_ENABLE_CHECK_EMPTY_LINE 38 | 39 | beginTest(); 40 | file.seekEnd(); 41 | file.addLine(); 42 | file.gotoBeginOfLine(); 43 | assertTrue(file.isEmptyLine()); 44 | file.gotoBeginOfLine(); 45 | file.write('\0'); 46 | file.write('\0'); 47 | file.gotoBeginOfLine(); 48 | assertTrue(file.isEmptyLine()); 49 | endTest(); 50 | 51 | #else 52 | skip(); 53 | return; 54 | #endif //CSV_FILE_ENABLE_CHECK_EMPTY_LINE 55 | } 56 | 57 | test(isEmptyLine_failed) 58 | { 59 | #if CSV_FILE_ENABLE_CHECK_EMPTY_LINE 60 | beginTest(); 61 | file.gotoBeginOfFile(); 62 | assertFalse(file.isEmptyLine()); 63 | endTest(); 64 | #else 65 | skip(); 66 | return; 67 | #endif //CSV_FILE_ENABLE_CHECK_EMPTY_LINE 68 | } 69 | 70 | test(gotoField_ok) 71 | { 72 | beginTest(); 73 | file.gotoBeginOfFile(); 74 | assertTrue(file.gotoField(1)); 75 | assertEqual(file.curPosition(), start1Line + length1Line1Field + lengthDelimiter); 76 | assertTrue(file.gotoField(2)); 77 | assertEqual(file.curPosition(), start1Line + length1Line1Field + lengthDelimiter + length1Line2Field + lengthDelimiter); 78 | 79 | file.nextLine(); 80 | file.seekCur(1); 81 | assertTrue(file.gotoField(1)); 82 | assertEqual(file.curPosition(), start2Line + length2Line1Field + lengthDelimiter); 83 | file.seekCur(2); 84 | assertTrue(file.gotoField(2)); 85 | assertEqual(file.curPosition(), start2Line + length2Line1Field + lengthDelimiter + length2Line2Field + lengthDelimiter); 86 | endTest(); 87 | } 88 | 89 | test(gotoField_failed) 90 | { 91 | beginTest(); 92 | file.gotoBeginOfFile(); 93 | assertFalse(file.gotoField(100)); 94 | assertTrue(file.isEndOfLine()); 95 | endTest(); 96 | } 97 | 98 | test(addField_empty_ok) 99 | { 100 | beginTest(); 101 | 102 | file.seekEnd(); //It function break numbering of line and fields. 103 | file.gotoBeginOfLine(); //It function fix fields numbering. 104 | file.nextField(); 105 | file.nextField(); 106 | file.nextField(); 107 | assertTrue(file.addField()); 108 | int beginOfCurrentField = file.curPosition(); 109 | assertTrue(file.gotoField(3)); 110 | assertEqual(file.curPosition(), beginOfCurrentField); 111 | 112 | file.seekCur(-1); 113 | assertEqual((char)file.read(), ';'); 114 | assertTrue(file.isEndOfFile()); 115 | 116 | assertTrue(file.addField()); 117 | beginOfCurrentField = file.curPosition(); 118 | assertTrue(file.gotoField(4)); 119 | assertEqual(file.curPosition(), beginOfCurrentField); 120 | assertTrue(file.addField()); 121 | beginOfCurrentField = file.curPosition(); 122 | assertTrue(file.gotoField(5)); 123 | assertEqual(file.curPosition(), beginOfCurrentField); 124 | assertTrue(file.addField()); 125 | beginOfCurrentField = file.curPosition(); 126 | assertTrue(file.gotoField(6)); 127 | assertEqual(file.curPosition(), beginOfCurrentField); 128 | file.seekCur(-2); 129 | assertEqual((char)file.read(), ';'); 130 | assertEqual((char)file.read(), ';'); 131 | assertTrue(file.isEndOfFile()); 132 | 133 | endTest(); 134 | } 135 | 136 | test(addField_empty_failed) 137 | { 138 | #if CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 139 | beginTest(); 140 | 141 | file.gotoBeginOfFile(); 142 | assertFalse(file.addField()); 143 | 144 | endTest(); 145 | #else 146 | skip(); 147 | return; 148 | #endif //CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 149 | } 150 | 151 | test(addField_num_non_fixed_size_failed) 152 | { 153 | #if CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 154 | beginTest(); 155 | 156 | file.gotoBeginOfFile(); 157 | assertFalse(file.addField(344)); 158 | 159 | endTest(); 160 | #else 161 | skip(); 162 | return; 163 | #endif //CSV_FILE_ENABLE_CHECK_OVERWRITE_ERROR 164 | } 165 | 166 | test(addField_num_non_fixed_size_ok) 167 | { 168 | beginTest(); 169 | file.seekEnd(); 170 | file.gotoBeginOfLine(); //It function fix fields numbering. 171 | file.nextField(); 172 | file.nextField(); 173 | file.nextField(); 174 | int beginOfCurrentField = file.curPosition() + 1; 175 | assertTrue(file.addField(123)); 176 | assertTrue(file.gotoField(3)); 177 | assertEqual(file.curPosition(), beginOfCurrentField); 178 | 179 | const int bufferSize = 3; 180 | char buffer_[bufferSize]; 181 | int bufferNum = -1; 182 | 183 | assertTrue(file.readField(bufferNum, buffer_, bufferSize)); 184 | assertEqual(bufferNum, 123); 185 | 186 | endTest(); 187 | } 188 | 189 | test(copyField_ok) 190 | { 191 | beginTest(); 192 | 193 | SdBaseFile * outFile = new SdBaseFile(); 194 | outFile->open(SECOND_UNIT_TEST_FILE, O_RDWR | O_CREAT); 195 | 196 | file.gotoBeginOfFile(); 197 | file.copyField(outFile); 198 | 199 | outFile->seekSet(0); 200 | 201 | const int bufferSize = 4; 202 | char buffer_[bufferSize]; 203 | 204 | outFile->read(buffer_, bufferSize); 205 | assertTrue(equalCharArrays(buffer_, "Ala")); 206 | 207 | outFile->remove(); 208 | outFile->close(); 209 | delete outFile; 210 | endTest(); 211 | } 212 | 213 | test(copyField_ok2) 214 | { 215 | beginTest(); 216 | 217 | SdBaseFile * outFile = new SdBaseFile(); 218 | outFile->open(SECOND_UNIT_TEST_FILE, O_RDWR | O_CREAT); 219 | 220 | file.gotoBeginOfFile(); 221 | file.copyField(outFile); 222 | file.nextField(); 223 | file.copyField(outFile); 224 | 225 | outFile->seekSet(0); 226 | 227 | const int bufferSize = 4; 228 | char buffer_[bufferSize]; 229 | 230 | outFile->read(buffer_, bufferSize); 231 | assertTrue(equalCharArrays(buffer_, "Alam")); 232 | 233 | outFile->read(buffer_, bufferSize); 234 | assertTrue(equalCharArrays(buffer_, "a")); 235 | 236 | outFile->remove(); 237 | outFile->close(); 238 | delete outFile; 239 | endTest(); 240 | } 241 | 242 | test(copyField_ok3) 243 | { 244 | beginTest(); 245 | 246 | SdBaseFile * outFile = new SdBaseFile(); 247 | outFile->open(SECOND_UNIT_TEST_FILE, O_RDWR | O_CREAT); 248 | 249 | file.gotoBeginOfFile(); 250 | file.copyField(outFile); 251 | file.nextField(); 252 | file.nextField(); 253 | file.copyField(outFile); 254 | 255 | outFile->seekSet(0); 256 | 257 | const int bufferSize = 4; 258 | char buffer_[bufferSize]; 259 | 260 | outFile->read(buffer_, bufferSize); 261 | assertTrue(equalCharArrays(buffer_, "Alak")); 262 | outFile->read(buffer_, bufferSize); 263 | assertTrue(equalCharArrays(buffer_, "ota")); 264 | 265 | outFile->remove(); 266 | outFile->close(); 267 | delete outFile; 268 | endTest(); 269 | } 270 | 271 | #endif //CSVFileTestPart6_h 272 | -------------------------------------------------------------------------------- /CSVFileTest/Config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef Config_h 11 | #define Config_h 12 | 13 | #include 14 | 15 | // SPI pinout 16 | #define PIN_SPI_CLK 13 17 | #define PIN_SPI_MOSI 11 18 | #define PIN_SPI_MISO 12 19 | 20 | #define PIN_SD_CS 10 21 | #define PIN_OTHER_SPI_DEVICE_CS 4 22 | 23 | // SD config 24 | #define SPI_SD_MAX_SPEED SPI_FULL_SPEED //SPI_QUARTER_SPEED //SPI_HALF_SPEED 25 | 26 | // SPI config 27 | #define SPI_USE_TRANSACTION 1 28 | //Remember set SD config in SD FAT Config 29 | 30 | void setupPinout() { 31 | pinMode(PIN_SD_CS, OUTPUT); 32 | pinMode(PIN_SPI_MOSI, OUTPUT); 33 | pinMode(PIN_SPI_MISO, INPUT); 34 | pinMode(PIN_SPI_CLK, OUTPUT); 35 | //Disable SPI devices 36 | digitalWrite(PIN_OTHER_SPI_DEVICE_CS, HIGH); 37 | digitalWrite(PIN_SD_CS, HIGH); 38 | } 39 | 40 | #endif //Config.h 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /CSVFileTest/UnitTestUtils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - unit test 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | #ifndef UnitTestUtils_h 11 | #define UnitTestUtils_h 12 | 13 | #define ANY_CHAR '`' 14 | bool equalCharArrays(const char * arr, const char * correctArr) 15 | { 16 | while (*correctArr) 17 | { 18 | if (! *arr) 19 | return false; 20 | 21 | if (*correctArr != ANY_CHAR) 22 | { 23 | if (*arr != *correctArr) 24 | return false; 25 | } 26 | *arr++; 27 | *correctArr++; 28 | } 29 | return true; 30 | } 31 | 32 | #endif //UnitTestUtils_h 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino-CSV-File 2 | Extend class for SdFat library for easy work with CSV files. 3 | 4 | ## Main features 5 | * Write CSV file 6 | CSV File provide functions for add CSV fields and lines without worrying 7 | about delimiters or end of line character. 8 | * Read CSV file 9 | CSV File designed for work with big files and memory limits. It encapsulate 10 | parsing process and return clear data. 11 | 12 | * Edit CSV file 13 | CSV File can modify data in field without problems with override value in next field. 14 | In this version support only numberic byte fields (range values 0-255). 15 | 16 | * Deleting content 17 | Lines in CSV file can be marked as deleted. In a further processing these lines are ignored. 18 | It isn't really deleting but it fast solution. 19 | 20 | * Navigation 21 | CSV File can easy move across the fields and the lines without worrying about position in file. 22 | 23 | * Support UTF-8 content 24 | CSV File correct work with non-ASCII characters (characters consist of two parts). 25 | 26 | ## How to use 27 | First you need download [SdFat](https://github.com/greiman/SdFat). 28 | It is only plug-in class for this library. 29 | SdFat is fine, light and fast library for support SD cards. 30 | It is better than standard Arduino SD library. 31 | 32 | After install SdFat you should put CSVFile repository in subfolder in your Arduino path. 33 | (On Windows probably: ~/Documents/Arduino/libraries/CSVFile) 34 | 35 | It is all. In Arduino IDE you can see examples how use CSVFile. 36 | (Menu File->Examples->CSVFile) to learn how use CSVFile. 37 | 38 | Example usage: 39 | 40 | ```cpp 41 | // SdFat library should be included before CSV File 42 | #include 43 | #include 44 | 45 | // Standard SdFat object for support SD card 46 | SdFat sd; 47 | // Instead of SdFile use CSV File 48 | CSVFile csv; 49 | 50 | void setup() { 51 | sd.begin(); // Initialize SD card 52 | // Important note! 53 | // You should use flag O_RDWR even if you use CSV File 54 | // only for writting. 55 | csv.open("file.csv", O_RDWR | O_CREAT); 56 | 57 | // See "HelloWorld.ino" for example more operations. 58 | csv.addField("Hello CSV!"); 59 | 60 | //Don't forget close file 61 | csv.close(); 62 | } 63 | 64 | void loop() { } 65 | ``` 66 | 67 | ## CSV structure 68 | CSV File assume that file consist of lines. Each line consist of fields. 69 | Each line without last is ended by end of line character in UNIX-style ('\n'). 70 | Fields are separate by single character delimiter (default it is semicolon: ';'). 71 | Lines can have different length. 72 | 73 | Example: 74 | 75 | > field1;field2;field3\n 76 | field4;field5\n 77 | field6;field7;field8;field9 78 | 79 | ## Known issues 80 | * CSV File support only UNIX-style end of line (\n). 81 | Arduino and SdFat use Windows-style end of line (\r\n). You should not use standard SdFat functions 82 | for write end of line character (e.g. println). 83 | * CSV File don't override any SdFat function. 84 | You should be carreful when you use standard functions 85 | manipulate position in file (e.g. seekSet, seekCur seekEnd, read, write, print), because they 86 | can corrupt line numbering and some functions (e.g. gotoLine, gotoField, getNumberOfLine) will not work properly. 87 | * Don't support standard Arduino SD library 88 | * Delete line feature don't work corectly with short line (default length less then 4). 89 | * CSV File has limitation for reading buffer to byte (255) size. 90 | * CSV File cannot convert numberic field when reading buffer size is less then number of digits reading value 91 | * CSV File can read only byte numberic field 92 | * CSV File can edit only numberic field 93 | * CSV File don't support add fields with content from flash memory (const character arrays created by using F() macro). 94 | * Cannot define configuration of CSV File from outside library 95 | * Missing method for check begin of field 96 | 97 | ## Author 98 | Slawomir "Fivitti" Figiel 99 | 100 | ## License 101 | CSVFile is under MIT license. -------------------------------------------------------------------------------- /examples/Benchmark/Benchmark.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /** 11 | * Benchmark reading and writing CSV file. 12 | * It is program similar to code from SdFat examples ("bench"). 13 | * @cSVFile is of course slower then clear SdFat file. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | // =*= CONFIG =*= 22 | // SPI pinout 23 | #define PIN_SPI_CLK 13 24 | #define PIN_SPI_MOSI 11 25 | #define PIN_SPI_MISO 12 26 | #define PIN_SD_CS 10 27 | // If you have connected other SPI device then 28 | // put here number of pin for disable its. 29 | // Provide -1 if you don't have other devices. 30 | #define PIN_OTHER_DEVICE_CS -1 31 | // Change this value if you have problems with SD card 32 | // Available values: SPI_QUARTER_SPEED //SPI_HALF_SPEED 33 | //It is enum from SdFat 34 | #define SD_CARD_SPEED SPI_FULL_SPEED 35 | 36 | #define FILENAME "CSV.csv" 37 | 38 | // =*= END CONFIG =*= 39 | 40 | // =*= BENCHMARK CONFIG =*= 41 | //CSVFile not support larger buffer then 254. 42 | const byte BUFFER_SIZE = 255-1; //Not contain end of line char 43 | const byte WRITE_COUNT = 5; 44 | const byte READ_COUNT = 5; 45 | // File size in MB where MB = 1,000,000 bytes. 46 | const byte FILE_SIZE_MB = 5; 47 | // =*= END BENCHMARK CONFIG =*= 48 | 49 | SdFat sd; 50 | CSVFile csv; 51 | 52 | // Serial output stream 53 | ArduinoOutStream cout(Serial); 54 | 55 | const uint32_t FILE_SIZE = 1000000UL * FILE_SIZE_MB; 56 | char buffer[BUFFER_SIZE + 1]; 57 | const unsigned int BUF_SIZE = BUFFER_SIZE + 1; 58 | 59 | void setup() { 60 | // Setup pinout 61 | pinMode(PIN_SPI_MOSI, OUTPUT); 62 | pinMode(PIN_SPI_MISO, INPUT); 63 | pinMode(PIN_SPI_CLK, OUTPUT); 64 | //Disable SPI devices 65 | pinMode(PIN_SD_CS, OUTPUT); 66 | digitalWrite(PIN_SD_CS, HIGH); 67 | 68 | #if PIN_OTHER_DEVICE_CS > 0 69 | pinMode(PIN_OTHER_DEVICE_CS, OUTPUT); 70 | digitalWrite(PIN_OTHER_DEVICE_CS, HIGH); 71 | #endif //PIN_OTHER_DEVICE_CS > 0 72 | 73 | // Setup serial 74 | Serial.begin(9600); 75 | while (!Serial) { /* wait for Leonardo */ } 76 | // Setup SD card 77 | if (!sd.begin(PIN_SD_CS, SD_CARD_SPEED)) 78 | { 79 | Serial.println("SD card begin error"); 80 | return; 81 | } 82 | 83 | buffer[BUFFER_SIZE] = '\0'; 84 | } 85 | 86 | void waitForKey() 87 | { 88 | while (Serial.read() >= 0) { } 89 | Serial.println(F("Type any character to repeat.\n")); 90 | while (Serial.read() <= 0) { } 91 | } 92 | 93 | void initSdFile() 94 | { 95 | if (sd.exists(FILENAME) && !sd.remove(FILENAME)) 96 | { 97 | Serial.println("Failed init remove file"); 98 | return; 99 | } 100 | if (!csv.open(FILENAME, O_RDWR | O_CREAT)) { 101 | Serial.println("Failed open file"); 102 | } 103 | } 104 | 105 | void loop() { 106 | float s; 107 | uint32_t t; 108 | uint32_t maxLatency; 109 | uint32_t minLatency; 110 | uint32_t totalLatency; 111 | 112 | initSdFile(); 113 | 114 | for(unsigned int i=0; i < BUFFER_SIZE; ++i) 115 | buffer[i] = 'A' + (i % 26); 116 | 117 | cout << F("File size ") << (int)FILE_SIZE_MB << F(" MB\n"); 118 | cout << F("Buffer size ") << BUF_SIZE << F(" bytes\n"); 119 | cout << F("Starting write test, please wait.") << endl << endl; 120 | 121 | // do write test 122 | uint32_t n = FILE_SIZE/sizeof(buffer); 123 | cout < m) { 143 | minLatency = m; 144 | } 145 | totalLatency += m; 146 | 147 | if (i < n - 1); 148 | csv.addLine(); 149 | } 150 | csv.sync(); 151 | t = millis() - t; 152 | s = csv.fileSize(); 153 | cout << s/t <<',' << maxLatency << ',' << minLatency; 154 | cout << ',' << totalLatency/n << endl; 155 | } 156 | 157 | cout << endl << F("Starting read test, please wait.") << endl; 158 | cout << endl < m) { 181 | minLatency = m; 182 | } 183 | totalLatency += m; 184 | csv.nextLine(); 185 | } 186 | t = millis() - t; 187 | cout << s/t <<',' << maxLatency << ',' << minLatency; 188 | cout << ',' << totalLatency/n << endl; 189 | } 190 | cout << endl << F("Done") << endl; 191 | 192 | // Don't forget close the file. 193 | csv.close(); 194 | 195 | waitForKey(); 196 | } 197 | -------------------------------------------------------------------------------- /examples/CSVFileSize/CSVFileSize.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /* 11 | * Program to compare size of CSVFile with SdFile from SdFat. 12 | * See SdFatSize.ino (in SdFat examples) for SdFat SD program. 13 | * 14 | */ 15 | #include 16 | #include 17 | 18 | SdFat sd; 19 | CSVFile csv; 20 | 21 | void setup() { 22 | Serial.begin(9600); 23 | while (!Serial) { /* wait for Leonardo */ } 24 | // Setup SD card 25 | if (!sd.begin()) 26 | { 27 | Serial.println("begin failed"); 28 | return; 29 | } 30 | 31 | csv.open("CSV.csv", O_RDWR | O_CREAT); 32 | csv.addField("Hello"); 33 | csv.close(); 34 | Serial.println("Done"); 35 | } 36 | 37 | void loop() {} 38 | -------------------------------------------------------------------------------- /examples/CopyCSV/CopyCSV.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /** 11 | * Example of copying fields feature. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | // =*= CONFIG =*= 18 | // SPI pinout 19 | #define PIN_SPI_CLK 13 20 | #define PIN_SPI_MOSI 11 21 | #define PIN_SPI_MISO 12 22 | #define PIN_SD_CS 10 23 | // If you have connected other SPI device then 24 | // put here number of pin for disable its. 25 | // Provide -1 if you don't have other devices. 26 | #define PIN_OTHER_DEVICE_CS -1 27 | // Change this value if you have problems with SD card 28 | // Available values: SPI_QUARTER_SPEED //SPI_HALF_SPEED 29 | //It is enum from SdFat 30 | #define SD_CARD_SPEED SPI_FULL_SPEED 31 | 32 | #define FILENAME "CSV.csv" 33 | #define FILENAME_TARGET "CSV_2.csv" 34 | 35 | // =*= END CONFIG =*= 36 | 37 | SdFat sd; 38 | CSVFile csv; 39 | CSVFile csvTarget; 40 | 41 | void setup() { 42 | // Setup pinout 43 | pinMode(PIN_SPI_MOSI, OUTPUT); 44 | pinMode(PIN_SPI_MISO, INPUT); 45 | pinMode(PIN_SPI_CLK, OUTPUT); 46 | //Disable SPI devices 47 | pinMode(PIN_SD_CS, OUTPUT); 48 | digitalWrite(PIN_SD_CS, HIGH); 49 | 50 | #if PIN_OTHER_DEVICE_CS > 0 51 | pinMode(PIN_OTHER_DEVICE_CS, OUTPUT); 52 | digitalWrite(PIN_OTHER_DEVICE_CS, HIGH); 53 | #endif //PIN_OTHER_DEVICE_CS > 0 54 | 55 | // Setup serial 56 | Serial.begin(9600); 57 | while (!Serial) { /* wait for Leonardo */ } 58 | // Setup SD card 59 | if (!sd.begin(PIN_SD_CS, SD_CARD_SPEED)) 60 | { 61 | Serial.println("SD card begin error"); 62 | return; 63 | } 64 | } 65 | 66 | void waitForKey() 67 | { 68 | while (Serial.read() >= 0) { } 69 | Serial.println(F("Type any character to repeat.\n")); 70 | while (Serial.read() <= 0) { } 71 | } 72 | 73 | void initSdFile() 74 | { 75 | if (sd.exists(FILENAME) && !sd.remove(FILENAME)) 76 | { 77 | Serial.println("Failed init remove file"); 78 | return; 79 | } 80 | if (!csv.open(FILENAME, O_RDWR | O_CREAT)) { 81 | Serial.println("Failed open file"); 82 | } 83 | 84 | if (!csvTarget.open(FILENAME_TARGET, O_RDWR | O_CREAT)) { 85 | Serial.println("Failed open target file"); 86 | } 87 | } 88 | 89 | void loop() { 90 | initSdFile(); 91 | 92 | // First we fill the file with content. 93 | // We use here standard SdFat function for write. 94 | // See "WriteCSV" example for write with CSVFile. 95 | 96 | csv.write("One;Two;Three\n"); 97 | csv.write("Four;Five;Six"); 98 | 99 | csv.gotoBeginOfFile(); 100 | csvTarget.gotoBeginOfFile(); 101 | 102 | //Copying file 1:1 from csv to csvTarget 103 | do 104 | { 105 | do 106 | { 107 | csv.copyField(&csvTarget); 108 | } 109 | while (csv.nextField() && csvTarget.addField()); 110 | } 111 | while (csv.nextLine() && csvTarget.addLine()); 112 | 113 | // Don't forget close the file. 114 | csv.close(); 115 | csvTarget.close(); 116 | 117 | waitForKey(); 118 | } 119 | -------------------------------------------------------------------------------- /examples/DeleteLine/DeleteLine.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /** 11 | * Mark line as delete feature example. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | // =*= CONFIG =*= 18 | // SPI pinout 19 | #define PIN_SPI_CLK 13 20 | #define PIN_SPI_MOSI 11 21 | #define PIN_SPI_MISO 12 22 | #define PIN_SD_CS 10 23 | // If you have connected other SPI device then 24 | // put here number of pin for disable its. 25 | // Provide -1 if you don't have other devices. 26 | #define PIN_OTHER_DEVICE_CS -1 27 | // Change this value if you have problems with SD card 28 | // Available values: SPI_QUARTER_SPEED //SPI_HALF_SPEED 29 | //It is enum from SdFat 30 | #define SD_CARD_SPEED SPI_FULL_SPEED 31 | 32 | #define FILENAME "CSV.csv" 33 | 34 | // =*= END CONFIG =*= 35 | 36 | SdFat sd; 37 | CSVFile csv; 38 | 39 | void setup() { 40 | // Setup pinout 41 | pinMode(PIN_SPI_MOSI, OUTPUT); 42 | pinMode(PIN_SPI_MISO, INPUT); 43 | pinMode(PIN_SPI_CLK, OUTPUT); 44 | //Disable SPI devices 45 | pinMode(PIN_SD_CS, OUTPUT); 46 | digitalWrite(PIN_SD_CS, HIGH); 47 | 48 | #if PIN_OTHER_DEVICE_CS > 0 49 | pinMode(PIN_OTHER_DEVICE_CS, OUTPUT); 50 | digitalWrite(PIN_OTHER_DEVICE_CS, HIGH); 51 | #endif //PIN_OTHER_DEVICE_CS > 0 52 | 53 | // Setup serial 54 | Serial.begin(9600); 55 | while (!Serial) { /* wait for Leonardo */ } 56 | // Setup SD card 57 | if (!sd.begin(PIN_SD_CS, SD_CARD_SPEED)) 58 | { 59 | Serial.println("SD card begin error"); 60 | return; 61 | } 62 | } 63 | 64 | void waitForKey() 65 | { 66 | while (Serial.read() >= 0) { } 67 | Serial.println(F("Type any character to repeat.\n")); 68 | while (Serial.read() <= 0) { } 69 | } 70 | 71 | void initSdFile() 72 | { 73 | if (sd.exists(FILENAME) && !sd.remove(FILENAME)) 74 | { 75 | Serial.println("Failed init remove file"); 76 | return; 77 | } 78 | if (!csv.open(FILENAME, O_RDWR | O_CREAT)) { 79 | Serial.println("Failed open file"); 80 | } 81 | } 82 | 83 | void loop() { 84 | // Data in CSV file is stored in lines. 85 | // Each line have some (or zero) fields. 86 | 87 | // Each line is ended by end line character '\n', 88 | // (UNIX style - without '\r'). 89 | 90 | // For enabled delete function go to CSVFile.h and set: 91 | // #define CSV_FILE_ENABLE_DELETING_LINE 1 92 | 93 | // We don't really remove characters from file. 94 | // We should overwrite deleted fragment by all tail file content, 95 | // but for large file it can during a long time. 96 | // For fast processing CSVFile has function which mark line as delelte, 97 | // but nor really remove it. 98 | // We overwrite beign of line with delete marker. When line is start 99 | // with delete marker we known, that this line should be ignored. 100 | 101 | // Warning! This function has line length limitation. For avoid conflicts 102 | // with correct content file the delete marker has size 4. 103 | // Line shorter then 4 characters cannot be deleted! Be careful, because 104 | // delete shorter line can overwrite data. 105 | // For debug you can enable function that deny overwrite data. You can 106 | // enabled it by define CSV_CHECK_OVERWRITE_ERROR. 107 | 108 | // Warning! For correct work this function position in file should be set 109 | // at begin of deleting line. 110 | 111 | initSdFile(); 112 | 113 | // First we fill the file with content. 114 | // We use here standard SdFat function for write. 115 | // See "WriteCSV" example for write with CSVFile. 116 | 117 | csv.write("OneLong\n"); 118 | csv.write("TwoLong\n"); 119 | csv.write("Three\n"); 120 | csv.write("Four\n"); // Mark as delete 121 | csv.write("Five\n"); 122 | csv.write("Six\n"); 123 | csv.write("Seven\n"); // Mark as delete 124 | csv.write("Eight\n"); 125 | csv.write("Nine\n"); 126 | csv.write("Ten"); 127 | 128 | csv.gotoBeginOfFile(); 129 | while (csv.nextLine()) ; //Skip 130 | Serial.print(F("Begin number of lines: ")); 131 | Serial.println(csv.getNumberOfLine() + 1); 132 | 133 | csv.gotoBeginOfFile(); 134 | csv.markLineAsDelete(); 135 | csv.nextLine(); 136 | csv.nextLine(); 137 | csv.markLineAsDelete(); 138 | csv.nextLine(); 139 | csv.markLineAsDelete(); 140 | 141 | csv.gotoBeginOfFile(); 142 | while (csv.nextLine()) ; //Skip 143 | Serial.print(F("After deleting number of lines: ")); 144 | Serial.println(csv.getNumberOfLine() + 1); 145 | 146 | // In your custom logic you can use function isLineMarkedAsDelete. 147 | // In standard this function isn't need, because pointer never set 148 | // at begin deleted line. 149 | // Warning! This function not set position in file at the begin of line. 150 | // After call you can do it manually. 151 | csv.seekSet(0); 152 | Serial.print(F("First line is deleted? ")); 153 | Serial.println(csv.isLineMarkedAsDelete() ? F("True") : F("False")); 154 | 155 | // Don't forget close the file. 156 | csv.close(); 157 | 158 | waitForKey(); 159 | } 160 | -------------------------------------------------------------------------------- /examples/EditCSV/EditCSV.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /** 11 | * Edit numberic field with fixed size feature example. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | // =*= CONFIG =*= 18 | // SPI pinout 19 | #define PIN_SPI_CLK 13 20 | #define PIN_SPI_MOSI 11 21 | #define PIN_SPI_MISO 12 22 | #define PIN_SD_CS 10 23 | // If you have connected other SPI device then 24 | // put here number of pin for disable its. 25 | // Provide -1 if you don't have other devices. 26 | #define PIN_OTHER_DEVICE_CS -1 27 | // Change this value if you have problems with SD card 28 | // Available values: SPI_QUARTER_SPEED //SPI_HALF_SPEED 29 | //It is enum from SdFat 30 | #define SD_CARD_SPEED SPI_FULL_SPEED 31 | 32 | #define FILENAME "CSV.csv" 33 | 34 | // =*= END CONFIG =*= 35 | 36 | SdFat sd; 37 | CSVFile csv; 38 | 39 | void setup() { 40 | // Setup pinout 41 | pinMode(PIN_SPI_MOSI, OUTPUT); 42 | pinMode(PIN_SPI_MISO, INPUT); 43 | pinMode(PIN_SPI_CLK, OUTPUT); 44 | //Disable SPI devices 45 | pinMode(PIN_SD_CS, OUTPUT); 46 | digitalWrite(PIN_SD_CS, HIGH); 47 | 48 | #if PIN_OTHER_DEVICE_CS > 0 49 | pinMode(PIN_OTHER_DEVICE_CS, OUTPUT); 50 | digitalWrite(PIN_OTHER_DEVICE_CS, HIGH); 51 | #endif //PIN_OTHER_DEVICE_CS > 0 52 | 53 | // Setup serial 54 | Serial.begin(9600); 55 | while (!Serial) { /* wait for Leonardo */ } 56 | // Setup SD card 57 | if (!sd.begin(PIN_SD_CS, SD_CARD_SPEED)) 58 | { 59 | Serial.println("SD card begin error"); 60 | return; 61 | } 62 | } 63 | 64 | void waitForKey() 65 | { 66 | while (Serial.read() >= 0) { } 67 | Serial.println(F("Type any character to repeat.\n")); 68 | while (Serial.read() <= 0) { } 69 | } 70 | 71 | void initSdFile() 72 | { 73 | if (sd.exists(FILENAME) && !sd.remove(FILENAME)) 74 | { 75 | Serial.println("Failed init remove file"); 76 | return; 77 | } 78 | if (!csv.open(FILENAME, O_RDWR | O_CREAT)) { 79 | Serial.println("Failed open file"); 80 | } 81 | } 82 | 83 | void loop() { 84 | // Data in CSV file is stored in lines. 85 | // Each line have some (or zero) fields. 86 | 87 | // Each line is ended by end line character '\n', 88 | // (UNIX style - without '\r'). 89 | 90 | // We cannot add new character in the middle of file. 91 | // We can only replace characters or add new at end 92 | // of file. 93 | // We assume that: 94 | // 1. Field for edit have constant size and it size is 95 | // known for programmer 96 | // 2. Field for edit is numberic field. 97 | 98 | initSdFile(); 99 | 100 | // First we fill the file with content. 101 | // We add one field with size 3. 102 | // CSVFile support (in this version) only 103 | // edit byte value. We can set new value 104 | // from 0 to 255. 105 | // 106 | // For size 2 we can set new value 0-99. 107 | // If we set greater then 99 value then 108 | // we override delimiter of file when edit. 109 | csv.addField(1, 3); 110 | csv.addField("not override"); 111 | csv.gotoBeginOfLine(); 112 | 113 | // Edit field 114 | csv.editField(255); 115 | 116 | // Don't forget close the file. 117 | csv.close(); 118 | 119 | waitForKey(); 120 | } 121 | -------------------------------------------------------------------------------- /examples/HelloWorld/HelloWorld.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /** 11 | * Simply example of write, read and edit CSV file. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | // =*= CONFIG =*= 18 | // SPI pinout 19 | #define PIN_SPI_CLK 13 20 | #define PIN_SPI_MOSI 11 21 | #define PIN_SPI_MISO 12 22 | #define PIN_SD_CS 10 23 | // If you have connected other SPI device then 24 | // put here number of pin for disable its. 25 | // Provide -1 if you don't have other devices. 26 | #define PIN_OTHER_DEVICE_CS -1 27 | // Change this value if you have problems with SD card 28 | // Available values: SPI_QUARTER_SPEED //SPI_HALF_SPEED 29 | //It is enum from SdFat 30 | #define SD_CARD_SPEED SPI_FULL_SPEED 31 | 32 | #define FILENAME "CSV.csv" 33 | 34 | // =*= END CONFIG =*= 35 | 36 | SdFat sd; 37 | CSVFile csv; 38 | 39 | void setup() { 40 | // Setup pinout 41 | pinMode(PIN_SPI_MOSI, OUTPUT); 42 | pinMode(PIN_SPI_MISO, INPUT); 43 | pinMode(PIN_SPI_CLK, OUTPUT); 44 | //Disable SPI devices 45 | pinMode(PIN_SD_CS, OUTPUT); 46 | digitalWrite(PIN_SD_CS, HIGH); 47 | 48 | #if PIN_OTHER_DEVICE_CS > 0 49 | pinMode(PIN_OTHER_DEVICE_CS, OUTPUT); 50 | digitalWrite(PIN_OTHER_DEVICE_CS, HIGH); 51 | #endif //PIN_OTHER_DEVICE_CS > 0 52 | 53 | // Setup serial 54 | Serial.begin(9600); 55 | while (!Serial) { /* wait for Leonardo */ } 56 | // Setup SD card 57 | if (!sd.begin(PIN_SD_CS, SD_CARD_SPEED)) 58 | { 59 | Serial.println("SD card begin error"); 60 | return; 61 | } 62 | } 63 | 64 | void waitForKey() 65 | { 66 | while (Serial.read() >= 0) { } 67 | Serial.println(F("Type any character to repeat.\n")); 68 | while (Serial.read() <= 0) { } 69 | } 70 | 71 | void initSdFile() 72 | { 73 | if (sd.exists(FILENAME) && !sd.remove(FILENAME)) 74 | { 75 | Serial.println("Failed init remove file"); 76 | return; 77 | } 78 | if (!csv.open(FILENAME, O_RDWR | O_CREAT)) { 79 | Serial.println("Failed open file"); 80 | } 81 | } 82 | 83 | void loop() { 84 | // Data in CSV file is stored in lines. 85 | // Each line have some (or zero) fields. 86 | 87 | // Each line is ended by end line character '\n', 88 | // (UNIX style - without '\r'). 89 | 90 | initSdFile(); 91 | 92 | // Create CSV 93 | csv.addField("Hello"); 94 | csv.addField("world!"); 95 | csv.addLine(); 96 | csv.addField(7, 2); 97 | 98 | // Read CSV 99 | csv.gotoBeginOfFile(); 100 | 101 | int bufferSize = 5; 102 | // Good practice is declare buffer larger by one then need 103 | // and put null char as end element. It is prevent by 104 | // overbuffer. 105 | char buffer[bufferSize + 1]; 106 | buffer[bufferSize] = '\0'; 107 | int numBuffer = 0; 108 | 109 | csv.readField(buffer, bufferSize); 110 | Serial.println(buffer); 111 | csv.nextField(); 112 | csv.readField(buffer, bufferSize); 113 | Serial.println(buffer); 114 | csv.nextLine(); 115 | csv.readField(numBuffer, buffer, bufferSize); 116 | Serial.println(numBuffer); 117 | 118 | // Edit CSV 119 | csv.gotoBeginOfField(); 120 | csv.editField(11); 121 | csv.gotoBeginOfField(); 122 | csv.readField(numBuffer, buffer, bufferSize); 123 | Serial.println(numBuffer); 124 | 125 | // Don't forget close the file. 126 | csv.close(); 127 | 128 | waitForKey(); 129 | } 130 | -------------------------------------------------------------------------------- /examples/Navigation/Navigation.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /** 11 | * Example navigation in CSV file. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | // =*= CONFIG =*= 18 | // SPI pinout 19 | #define PIN_SPI_CLK 13 20 | #define PIN_SPI_MOSI 11 21 | #define PIN_SPI_MISO 12 22 | #define PIN_SD_CS 10 23 | // If you have connected other SPI device then 24 | // put here number of pin for disable its. 25 | // Provide -1 if you don't have other devices. 26 | #define PIN_OTHER_DEVICE_CS -1 27 | // Change this value if you have problems with SD card 28 | // Available values: SPI_QUARTER_SPEED //SPI_HALF_SPEED 29 | //It is enum from SdFat 30 | #define SD_CARD_SPEED SPI_FULL_SPEED 31 | 32 | #define FILENAME "CSV.csv" 33 | 34 | // =*= END CONFIG =*= 35 | 36 | SdFat sd; 37 | CSVFile csv; 38 | 39 | void setup() { 40 | // Setup pinout 41 | pinMode(PIN_SPI_MOSI, OUTPUT); 42 | pinMode(PIN_SPI_MISO, INPUT); 43 | pinMode(PIN_SPI_CLK, OUTPUT); 44 | //Disable SPI devices 45 | pinMode(PIN_SD_CS, OUTPUT); 46 | digitalWrite(PIN_SD_CS, HIGH); 47 | 48 | #if PIN_OTHER_DEVICE_CS > 0 49 | pinMode(PIN_OTHER_DEVICE_CS, OUTPUT); 50 | digitalWrite(PIN_OTHER_DEVICE_CS, HIGH); 51 | #endif //PIN_OTHER_DEVICE_CS > 0 52 | 53 | // Setup serial 54 | Serial.begin(9600); 55 | while (!Serial) { /* wait for Leonardo */ } 56 | // Setup SD card 57 | if (!sd.begin(PIN_SD_CS, SD_CARD_SPEED)) 58 | { 59 | Serial.println("SD card begin error"); 60 | return; 61 | } 62 | } 63 | 64 | void waitForKey() 65 | { 66 | while (Serial.read() >= 0) { } 67 | Serial.println(F("Type any character to repeat.\n")); 68 | while (Serial.read() <= 0) { } 69 | } 70 | 71 | void initSdFile() 72 | { 73 | if (sd.exists(FILENAME) && !sd.remove(FILENAME)) 74 | { 75 | Serial.println("Failed init remove file"); 76 | return; 77 | } 78 | if (!csv.open(FILENAME, O_RDWR | O_CREAT)) { 79 | Serial.println("Failed open file"); 80 | } 81 | } 82 | 83 | void loop() { 84 | // Data in CSV file is stored in lines. 85 | // Each line have some (or zero) fields. 86 | 87 | // Each line is ended by end line character '\n', 88 | // (UNIX style - without '\r'). 89 | 90 | // Warning! For correct work functions gotoLine and gotoField 91 | // you should be careful when you use standard SdFile function 92 | // (as e.g: seekCur, seekEnd, read, print), because can corrupt 93 | // line numbering (any standard function are not overloading 94 | // for correct work with numbering). 95 | 96 | initSdFile(); 97 | 98 | // First we fill the file with content. 99 | // We use here standard SdFat function for write. 100 | // See "WriteCSV" example for write with CSVFile. 101 | 102 | csv.write("One;Two;Three\n"); 103 | csv.write("Four;Five;Six\n"); 104 | csv.write("Seven;Eight;Nine\n"); 105 | csv.write("Ten;Eleven;Twelve"); 106 | 107 | // === Navigation in file === 108 | 109 | // This function set position in file at the begin 110 | csv.gotoBeginOfFile(); 111 | // Now we are in first line. For go to next line: 112 | csv.nextLine(); 113 | // Now we are in second line. 114 | csv.nextLine(); 115 | // Now we are in thrird line 116 | csv.nextLine(); 117 | // Now we are in fourth line. It is last line. Check it. 118 | if (csv.nextLine()) 119 | Serial.println(F("Oh no! Something is wrong!")); 120 | else 121 | Serial.println(F("It is last line.")); 122 | 123 | // When function nextLine return false pointer is at the end of file 124 | // We can check it: 125 | if (csv.isEndOfFile()) 126 | Serial.println(F("End of file.")); 127 | 128 | // We can go to specific line. Line are numbered from zero. 129 | csv.gotoLine(0); //First line 130 | csv.gotoLine(2); //Third line 131 | csv.gotoLine(1); //Second line 132 | 133 | // If CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH define is enabled 134 | // then we can go to line which begin with specific string 135 | // It search from current position then we must call gotoBeginOfFile 136 | #if CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 137 | csv.gotoBeginOfFile(); 138 | csv.gotoLine("One"); // First line 139 | csv.gotoBeginOfFile(); 140 | csv.gotoLine("Fo"); // Second line 141 | #endif //CSV_FILE_ENABLE_GOTO_BEGIN_STARTS_WITH 142 | 143 | // === Navigation in line === 144 | // We can easy navigate to begin of line by call 145 | csv.gotoBeginOfLine(); 146 | // We can check if we are at begin of line: 147 | if (csv.isBeginOfLine()) 148 | Serial.println(F("Begin of line.")); 149 | // For go to end of line we use trick. 150 | // It is UNSAFE and corrupt numbering lines and fields 151 | csv.nextLine(); 152 | csv.seekCur(-1); // <-- Here we can invalid numbering 153 | // We are at end of line we can check it: 154 | if (csv.isEndOfLine()) 155 | Serial.println(F("End of line.")); 156 | // Ok, go back to begin of line. 157 | csv.gotoBeginOfLine(); // <-- This function repair field numbering, but not line! 158 | // We can move in fields. We are now in first field secon line ("Four") 159 | csv.nextField(); 160 | // We are now in "Five" 161 | csv.nextField(); 162 | // We are now in "Six". It is last field. 163 | // If we call now nextField then function return false and position 164 | // in file will be set at the end of line 165 | if (!csv.nextField() && csv.isEndOfLine()) 166 | Serial.println(F("End field, end of line")); 167 | else 168 | Serial.println(F("Oh, no! Something is wrong!")); 169 | 170 | // We can navigate in line by number of field. 171 | // Fields are numbering from zero. 172 | csv.gotoField(0); //First field ("Four"); 173 | csv.gotoField(2); //Third field ("Six"); 174 | csv.gotoField(1); //Second fild ("Five"); 175 | 176 | // === Navigation in field === 177 | // We can go to begin of field: 178 | csv.gotoBeginOfField(); 179 | // Function for check if is begin of field is missing in this version 180 | // but we can check if it it end of field 181 | // First we should go to end of field. We use trick. This 182 | // trick corupted field numbering 183 | csv.seekCur(-1); 184 | // We are now at end of first field. Check it: 185 | if (csv.isEndOfField()) 186 | Serial.println(F("End field")); 187 | else 188 | Serial.println(F("Oh, no! Something is wrong!")); 189 | 190 | // Don't forget close the file. 191 | csv.close(); 192 | 193 | waitForKey(); 194 | } 195 | -------------------------------------------------------------------------------- /examples/NumberingLine/NumberingLine.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /** 11 | * Numbering line feature example. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | // =*= CONFIG =*= 18 | // SPI pinout 19 | #define PIN_SPI_CLK 13 20 | #define PIN_SPI_MOSI 11 21 | #define PIN_SPI_MISO 12 22 | #define PIN_SD_CS 10 23 | // If you have connected other SPI device then 24 | // put here number of pin for disable its. 25 | // Provide -1 if you don't have other devices. 26 | #define PIN_OTHER_DEVICE_CS -1 27 | // Change this value if you have problems with SD card 28 | // Available values: SPI_QUARTER_SPEED //SPI_HALF_SPEED 29 | //It is enum from SdFat 30 | #define SD_CARD_SPEED SPI_FULL_SPEED 31 | 32 | #define FILENAME "CSV.csv" 33 | 34 | // =*= END CONFIG =*= 35 | 36 | SdFat sd; 37 | CSVFile csv; 38 | 39 | void setup() { 40 | // Setup pinout 41 | pinMode(PIN_SPI_MOSI, OUTPUT); 42 | pinMode(PIN_SPI_MISO, INPUT); 43 | pinMode(PIN_SPI_CLK, OUTPUT); 44 | //Disable SPI devices 45 | pinMode(PIN_SD_CS, OUTPUT); 46 | digitalWrite(PIN_SD_CS, HIGH); 47 | 48 | #if PIN_OTHER_DEVICE_CS > 0 49 | pinMode(PIN_OTHER_DEVICE_CS, OUTPUT); 50 | digitalWrite(PIN_OTHER_DEVICE_CS, HIGH); 51 | #endif //PIN_OTHER_DEVICE_CS > 0 52 | 53 | // Setup serial 54 | Serial.begin(9600); 55 | while (!Serial) { /* wait for Leonardo */ } 56 | // Setup SD card 57 | if (!sd.begin(PIN_SD_CS, SD_CARD_SPEED)) 58 | { 59 | Serial.println("SD card begin error"); 60 | return; 61 | } 62 | } 63 | 64 | void waitForKey() 65 | { 66 | while (Serial.read() >= 0) { } 67 | Serial.println(F("Type any character to repeat.\n")); 68 | while (Serial.read() <= 0) { } 69 | } 70 | 71 | void initSdFile() 72 | { 73 | if (sd.exists(FILENAME) && !sd.remove(FILENAME)) 74 | { 75 | Serial.println("Failed init remove file"); 76 | return; 77 | } 78 | if (!csv.open(FILENAME, O_RDWR | O_CREAT)) { 79 | Serial.println("Failed open file"); 80 | } 81 | } 82 | 83 | void loop() { 84 | // Data in CSV file is stored in lines. 85 | // Each line have some (or zero) fields. 86 | 87 | // Each line is ended by end line character '\n', 88 | // (UNIX style - without '\r'). 89 | 90 | // You can get number of current line by call @getNumbeOfLine 91 | // function. 92 | // 93 | // If file contains lines marked as delete then these lines will 94 | // be ignored. 95 | // For enabled delete function go to CSVFile.h and set: 96 | // #define CSV_FILE_ENABLE_DELETING_LINE 1 97 | // If this define is zero (default) then this example display 98 | // 10 numbers else 8 numbers. 99 | 100 | // Numbering is start from zero. 101 | 102 | // Warning! For correct work this function you should be careful 103 | // when you use standard SdFile function (as e.g: seekCur, seekEnd, 104 | // read, print), because can corrupt line numbering (any standard 105 | // function are not overloading for correct work with numbering). 106 | 107 | initSdFile(); 108 | 109 | // First we fill the file with content. 110 | // We use here standard SdFat function for write. 111 | // See "WriteCSV" example for write with CSVFile. 112 | 113 | csv.write("One\n"); 114 | csv.write("Two\n"); 115 | csv.write("Three\n"); 116 | csv.write("@DELFour\n"); // Mark as delete 117 | csv.write("Five\n"); 118 | csv.write("Six\n"); 119 | csv.write("@DELSeven\n"); // Mark as delete 120 | csv.write("Eight\n"); 121 | csv.write("Nine\n"); 122 | csv.write("Ten"); 123 | 124 | // This function set line counter to zero. 125 | csv.gotoBeginOfFile(); 126 | 127 | do 128 | { 129 | Serial.println(csv.getNumberOfLine()); 130 | } 131 | while (csv.nextLine()); 132 | 133 | // If CSV_FILE_ENABLE_DELETING_LINE define is zero (default) 134 | // then this example display 10 numbers else 8 numbers. 135 | 136 | // Don't forget close the file. 137 | csv.close(); 138 | 139 | waitForKey(); 140 | } 141 | -------------------------------------------------------------------------------- /examples/ReadCSV/ReadCSV.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /** 11 | * Read CSV fields feature examples. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | // =*= CONFIG =*= 18 | // SPI pinout 19 | #define PIN_SPI_CLK 13 20 | #define PIN_SPI_MOSI 11 21 | #define PIN_SPI_MISO 12 22 | #define PIN_SD_CS 10 23 | // If you have connected other SPI device then 24 | // put here number of pin for disable its. 25 | // Provide -1 if you don't have other devices. 26 | #define PIN_OTHER_DEVICE_CS -1 27 | // Change this value if you have problems with SD card 28 | // Available values: SPI_QUARTER_SPEED //SPI_HALF_SPEED 29 | //It is enum from SdFat 30 | #define SD_CARD_SPEED SPI_FULL_SPEED 31 | 32 | #define FILENAME "CSV.csv" 33 | 34 | // =*= END CONFIG =*= 35 | 36 | SdFat sd; 37 | CSVFile csv; 38 | 39 | void setup() { 40 | // Setup pinout 41 | pinMode(PIN_SPI_MOSI, OUTPUT); 42 | pinMode(PIN_SPI_MISO, INPUT); 43 | pinMode(PIN_SPI_CLK, OUTPUT); 44 | //Disable SPI devices 45 | pinMode(PIN_SD_CS, OUTPUT); 46 | digitalWrite(PIN_SD_CS, HIGH); 47 | 48 | #if PIN_OTHER_DEVICE_CS > 0 49 | pinMode(PIN_OTHER_DEVICE_CS, OUTPUT); 50 | digitalWrite(PIN_OTHER_DEVICE_CS, HIGH); 51 | #endif //PIN_OTHER_DEVICE_CS > 0 52 | 53 | // Setup serial 54 | Serial.begin(9600); 55 | while (!Serial) { /* wait for Leonardo */ } 56 | // Setup SD card 57 | if (!sd.begin(PIN_SD_CS, SD_CARD_SPEED)) 58 | { 59 | Serial.println("SD card begin error"); 60 | return; 61 | } 62 | } 63 | 64 | void waitForKey() 65 | { 66 | while (Serial.read() >= 0) { } 67 | Serial.println(F("Type any character to repeat.\n")); 68 | while (Serial.read() <= 0) { } 69 | } 70 | 71 | void initSdFile() 72 | { 73 | if (sd.exists(FILENAME) && !sd.remove(FILENAME)) 74 | { 75 | Serial.println("Failed init remove file"); 76 | return; 77 | } 78 | if (!csv.open(FILENAME, O_RDWR | O_CREAT)) { 79 | Serial.println("Failed open file"); 80 | } 81 | } 82 | 83 | void loop() { 84 | // Data in CSV file is stored in lines. 85 | // Each line have some (or zero) fields. 86 | 87 | // Each line is ended by end line character '\n', 88 | // (UNIX style - without '\r'). 89 | // You shouldn't use "readln" method (and similars). 90 | // The fields are separated by delimiter ';'. 91 | // You can change this character in source file. 92 | // Your CSV file shouldn't contain this characters. 93 | 94 | initSdFile(); 95 | 96 | // First we fill the file with content. 97 | // We use here standard SdFat function for write. 98 | // See "WriteCSV" example for write with CSVFile. 99 | 100 | csv.write("One;Two;Three\n"); 101 | csv.write("Very long long field.;Second long field.\n"); 102 | csv.write("zażółć gęślą jaźń\n"); 103 | csv.write("11;22\0;3df;null\n"); 104 | csv.write(";"); 105 | 106 | csv.gotoBeginOfFile(); 107 | 108 | // We are now at the begin of file. 109 | // We need buffer for content fields. 110 | // 111 | // Buffer can have any size. 112 | // 113 | // Good practice is declare buffer size larger by one 114 | // then we need and put as last null character. 115 | // It is prevent from overbuffer. 116 | const byte BUFFER_SIZE = 5; 117 | char buffer[BUFFER_SIZE + 1]; 118 | buffer[BUFFER_SIZE] = '\0'; 119 | 120 | // We can read three types of field 121 | // 1. Text field with string content. 122 | // This field don't have size limit of content. 123 | // If buffer has size less then size of filed then 124 | // we need read field until read function return false 125 | // 126 | // First line - short fields 127 | csv.readField(buffer, BUFFER_SIZE); 128 | Serial.print(buffer); Serial.print(';'); 129 | // Function read set position in file at last char in field 130 | // (before delimiter or end of line). We must call function 131 | // for go to next field. 132 | csv.nextField(); 133 | csv.readField(buffer, BUFFER_SIZE); 134 | Serial.print(buffer); Serial.print(';'); 135 | csv.nextField(); 136 | csv.readField(buffer, BUFFER_SIZE); 137 | Serial.println(buffer); 138 | 139 | // Second line - long fields 140 | csv.nextLine(); 141 | while (csv.readField(buffer, BUFFER_SIZE)) 142 | { 143 | Serial.print(buffer); 144 | } 145 | Serial.print(';'); 146 | csv.nextField(); 147 | while (csv.readField(buffer, BUFFER_SIZE)) 148 | { 149 | Serial.print(buffer); 150 | } 151 | Serial.println(); 152 | 153 | // Third line - UTF-8 fields 154 | // This project support UTF-8 strings. We should 155 | // remeber that each non-ASCII character is stored 156 | // as pair bytes. You need sufficiently large buffer 157 | // (minimum size: 2). 158 | // Note: Serial don't support UTF-8 and you can see 159 | // random characters. 160 | csv.nextLine(); 161 | while (csv.readField(buffer, BUFFER_SIZE)) 162 | { 163 | Serial.print(buffer); 164 | } 165 | Serial.println(); 166 | 167 | // 2. Numberic field. For read this fields 168 | // us buffer should has size equal or greater then 169 | // number of digits reading number. 170 | // Minimal buffer for bytes: 3 (range from 0 to 255) 171 | // Minimal buffer for int: 5 (range from 0 to 65535) 172 | // This version of library don't support larger types. 173 | csv.nextLine(); 174 | // We need temporary variable for store result convertion 175 | int numBuffer = 0; 176 | 177 | // Only number in field: 178 | csv.readField(numBuffer, buffer, BUFFER_SIZE); 179 | Serial.print(numBuffer); Serial.print(';'); 180 | // Number and null char: 181 | csv.nextField(); 182 | csv.readField(numBuffer, buffer, BUFFER_SIZE); 183 | Serial.print(numBuffer); Serial.print(';'); 184 | // Number and literal stuff (only begin digits are important) 185 | csv.nextField(); 186 | csv.readField(numBuffer, buffer, BUFFER_SIZE); 187 | Serial.print(numBuffer); Serial.print(';'); 188 | // Missing digits only string. 189 | // Warning! Value of num buffer is set to zero, 190 | // but readField function return false! 191 | csv.nextField(); 192 | if (csv.readField(numBuffer, buffer, BUFFER_SIZE)) 193 | Serial.println(numBuffer); 194 | else 195 | Serial.println(F("Missing digits!")); 196 | 197 | // 3. Empty lines, without content 198 | csv.nextField(); 199 | csv.readField(buffer, BUFFER_SIZE); 200 | Serial.print(buffer); Serial.print(';'); 201 | csv.nextField(); 202 | csv.readField(buffer, BUFFER_SIZE); 203 | Serial.println(buffer); 204 | 205 | // Don't forget close the file. 206 | csv.close(); 207 | 208 | waitForKey(); 209 | } 210 | -------------------------------------------------------------------------------- /examples/WriteCSV/WriteCSV.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: CSVFile - examples 3 | * @author: Slawomir Figiel 4 | * @contact: fivitti@gmail.com 5 | * @date: 14.08.2016 6 | * @version: 1.0.1 7 | * @license: MIT 8 | */ 9 | 10 | /** 11 | * Write CSV fields feature example. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | // =*= CONFIG =*= 18 | // SPI pinout 19 | #define PIN_SPI_CLK 13 20 | #define PIN_SPI_MOSI 11 21 | #define PIN_SPI_MISO 12 22 | #define PIN_SD_CS 10 23 | // If you have connected other SPI device then 24 | // put here number of pin for disable its. 25 | // Provide -1 if you don't have other devices. 26 | #define PIN_OTHER_DEVICE_CS -1 27 | // Change this value if you have problems with SD card 28 | // Available values: SPI_QUARTER_SPEED //SPI_HALF_SPEED 29 | //It is enum from SdFat 30 | #define SD_CARD_SPEED SPI_FULL_SPEED 31 | 32 | #define FILENAME "CSV.csv" 33 | 34 | // =*= END CONFIG =*= 35 | 36 | SdFat sd; 37 | CSVFile csv; 38 | 39 | void setup() { 40 | // Setup pinout 41 | pinMode(PIN_SPI_MOSI, OUTPUT); 42 | pinMode(PIN_SPI_MISO, INPUT); 43 | pinMode(PIN_SPI_CLK, OUTPUT); 44 | //Disable SPI devices 45 | pinMode(PIN_SD_CS, OUTPUT); 46 | digitalWrite(PIN_SD_CS, HIGH); 47 | 48 | #if PIN_OTHER_DEVICE_CS > 0 49 | pinMode(PIN_OTHER_DEVICE_CS, OUTPUT); 50 | digitalWrite(PIN_OTHER_DEVICE_CS, HIGH); 51 | #endif //PIN_OTHER_DEVICE_CS > 0 52 | 53 | // Setup serial 54 | Serial.begin(9600); 55 | while (!Serial) { /* wait for Leonardo */ } 56 | // Setup SD card 57 | if (!sd.begin(PIN_SD_CS, SD_CARD_SPEED)) 58 | { 59 | Serial.println("SD card begin error"); 60 | return; 61 | } 62 | } 63 | 64 | void waitForKey() 65 | { 66 | while (Serial.read() >= 0) { } 67 | Serial.println(F("Type any character to repeat.\n")); 68 | while (Serial.read() <= 0) { } 69 | } 70 | 71 | void initSdFile() 72 | { 73 | if (sd.exists(FILENAME) && !sd.remove(FILENAME)) 74 | { 75 | Serial.println("Failed init remove file"); 76 | return; 77 | } 78 | // Important note! 79 | // You should use flag O_RDWR even if you use CSV File 80 | // only for writting. 81 | if (!csv.open(FILENAME, O_RDWR | O_CREAT)) { 82 | Serial.println("Failed open file"); 83 | } 84 | } 85 | 86 | void loop() { 87 | // Data in CSV file is stored in lines. 88 | // Each line have some (or zero) fields. 89 | // First you should add line and next 90 | // add fields. After you can add next line. 91 | 92 | // Each line is ended by end line character '\n', 93 | // (UNIX style - without '\r'). 94 | // You shouldn't use "println" method (and similars). 95 | // The fields are separated by delimiter ';'. 96 | // You can change this character in source file. 97 | // Your CSV file shouldn't contain this characters. 98 | 99 | // Important note! 100 | // You should use flag O_RDWR for initialize CSV File even if you use CSV File 101 | // only for writting. 102 | 103 | initSdFile(); 104 | 105 | // At the begin of file we don't need 106 | // add new line. 107 | 108 | // We can add four types of field 109 | // 1. Text field with string (const char array) 110 | // content. 111 | // Function don't have size limit of content. 112 | // Current version library don't support string 113 | // stored in flash memory. 114 | csv.addField("One"); 115 | csv.addField("Two"); 116 | csv.addField("Three"); 117 | // This project support UTF-8 strings, but process 118 | // single bytes (one UTF-8 character is represented 119 | // by two bytes. 120 | csv.addField("zażółć"); 121 | csv.addField("gęślą"); 122 | csv.addField("jaźń"); 123 | 124 | //Next line 125 | csv.addLine(); 126 | 127 | // 2. Number field with non-fixed size. 128 | // Use this field if you don't need 129 | // edit field's value later. 130 | // Support only positive integers. 131 | // It is function designed for write 132 | // line numbers. 133 | csv.addField((unsigned int)0); 134 | csv.addField(65535); 135 | csv.addField(3444); 136 | 137 | csv.addLine(); 138 | 139 | // 3. Number field with fixed size. 140 | // Use this field if you need edit 141 | // field's value later. 142 | // At the moment support only byte 143 | // values (from 0 to 255). 144 | // You should be sure, that size of field 145 | // is equals or greater then right number size. 146 | csv.addField(4, 1); 147 | csv.addField(4, 2); 148 | csv.addField(4, 3); 149 | csv.addField(55, 2); 150 | csv.addField(55, 3); 151 | csv.addField(255, 3); 152 | 153 | csv.addLine(); 154 | 155 | // 4. Empty field. 156 | // Use this field if you want add special content 157 | // using standard function from SdFat or if you 158 | // copy field from other file 159 | csv.addField(); 160 | csv.write("Hello"); //Standard function for SdFile, but you should prefer csv.addField("Hello") 161 | csv.addField(); 162 | csv.write("world"); 163 | //Coping field - commented, because we don't have other CSV file 164 | //csv.addField(); 165 | //otherCsv.copyField(csv); 166 | 167 | // We don't add empty line at the end of file. 168 | // CSV file shouldn't end by '\n' char. 169 | 170 | // Don't forget close the file. 171 | csv.close(); 172 | 173 | // After this operations your CSV file should look like this 174 | // ('\0' is null character): 175 | /* One;Two:Three;zazółć;gęślą;jaźń\n 176 | * 0;65535;3444\n 177 | * 4;4\0;4\0\0;55;55\0;777\n 178 | * Hello;world 179 | */ 180 | 181 | waitForKey(); 182 | } 183 | --------------------------------------------------------------------------------