Mapping a SDcard to an array

Dellyjoe

Active member
Hello everyone I'm going to do my best to explain the issue I'm having.

Currently I'm building a chess board that will allows the user to import a new chess puzzle in a .csv file format. which looks like this

8 WK
7 WP WP
6 WP BB QW
5 WP
4 WP
3 BP BB BP
2 BP WR WR BP
1 BK BR BP
a b c d e f g h

I need to mapp the above chess board to induvial LEDs to light up each spot that would need a piece to be placed on and then to 8 mux channels so I can tell if a piece is in the right spot. After the user puts the right pieces on the right spots I will also check to see if there is a piece in the right spot, and then clear all LEDs and tell the user which color he/she is playing.


The user will use a pot to select which puzzle he/she would like to do and then the program will open that said file. I have that part done but Can't seem to find how to record the cells to a array variable.

I found this code online but can't really use it b/c I don't understand it. Can someone explain to me what this code is doing?

Here is my sdcard.cpp file which reads and is working for me.

Code:
void SDcard::open_file(int indexer)
{
    filename_index = indexer;

    if (!SD.begin(chipSelect))
    {
        while (true);
    }
    File dataFile = SD.open(str_puzzle_name[filename_index]); //opening File T015704.csv

    // if the file is available, write to it:

    if (dataFile)
    {
        while (dataFile.available())
        {
            Serial.write(dataFile.read());
        }
        dataFile.close();
    }

    // if the file isn't open, pop up an error:

    else
    {
        Serial.println("error opening file");
    }


    
} // end openfile

Here is the code that I found on https://forum.arduino.cc/index.php?topic=210904.0

I will do my best to explain the code below. I will use the // and write each line to what I think it is doing.

Code:
   char buffer[40];  // defining an array with input being 40 char long
   byte index = 0; // defining a variable called index and setting it to 0
   while (myFile.available()) 
   {
   	char c = myFile.read(); // defining char c to equal the file you want to read
       if(c == '\n' || c == '\r') 
       {
           parseAndSave(buffer);
           index = 0;
           buffer[index] = '\0'; 
       }
       else
       {
           buffer[index++] = c;
           buffer[index] = '\0';
       }
   }

That is about all I can do with my current knowledge.

Thank you for taking the time out of your day to help me learning.

Joe
 
What version of TeensyDuino is in use? If using the TD 1.54 Beta 7 the SD code actually uses installed SdFat. Though SdFat is included - but uses different functions and methods.

Not sure if this help to answer - or maybe leads to new questions ...

If the data file is a complete CSV formatted file - like ( comma count may be off ) - where each line end with a '\n'=newline, and there will be 8 lines:
Code:
,,WK,,,,,,
,WP,,,,,,WP,
....WP, BB, QW
WP,,,,,,,
,,,,,,,WP
,,BP,,BB,,BP
BP,,WR,,WR,, BP
BK, BR,,,BP,,,

Each line should account for 8 fields, 7 commas - though trailing commas could be dealt with as not present leaving the space(s) open when empty squares

Have just been looking at SD & SdFat in TD 1.54 there is this example in SdFat library: ...\hardware\teensy\avr\libraries\SdFat\examples\ReadCsvFile\ReadCsvFile.ino
>> The File IO works - but uses SdFat funcs() instead of SD - so don't mix and match { like I did :( in starting }

It has code to Parse a CSV file:
Code:
bool parseLine(char* str) {
  char* ptr;

  // Set strtok start of line.
  str = strtok(str, ",");
  if (!str) return false;
  
  // Print text field.
  Serial.println(str);
  
  // Subsequent calls to strtok expects a null pointer.
  str = strtok(nullptr, ",");
  if (!str) return false;
  
  // Convert string to long integer.
  int32_t i32 = strtol(str, &ptr, 0);
  if (str == ptr || *skipSpace(ptr)) return false;
  Serial.println(i32);
  
  str = strtok(nullptr, ",");
  if (!str) return false;
  
  // strtoul accepts a leading minus with unexpected results.
  if (*skipSpace(str) == '-') return false;
  
  // Convert string to unsigned long integer.
  uint32_t u32 = strtoul(str, &ptr, 0);
  if (str == ptr || *skipSpace(ptr)) return false;
  Serial.println(u32);

  str = strtok(nullptr, ",");
  if (!str) return false;
  
  // Convert string to double.
  double d = strtod(str, &ptr);
  if (str == ptr || *skipSpace(ptr)) return false;
  Serial.println(d);
  
  // Check for extra fields.
  return strtok(nullptr, ",") == nullptr;
}

Looking at that sample the above parseLine() is passed a string for processing.
It would need alteration to recognize the Chess notations, i.e. set the board space not just print.
for instance it needs int countCol=0 that increments each token/comma field to map into the board[Row][countCol]
- and would also needs to know what line# so it can apply the parsing to the right Row.
Code:
parseLineCHESS( char* str, int Row ){
int countCol=0;

// ...
}

PsortaCode:
Code:
char board[8][8]; // global or pass into parseLineCHESS()
for ( int ii=0; ii<8; ii++) // empty the board
 for ( int jj=0; jj<8; jj++)
   board[ii][jj]=0;
open file
for ( int ii=0; ii<8; ii++) {
  read line
  parseLineCHESS( line, ii ); // this would parse each line and mark the occupied space in board[][] as it counts though the 'comma' fields
  // it could simply change 0 to 1 as it goes (to denote LED OFF/ON] or could decode the 6 piece types [P,Ki,Q,R,Kn,B] and differentiate them by color where White [1-6] and Black [11-16]
}
 
Last edited:
What version of TeensyDuino is in use? If using the TD 1.47 Beta 7 the SD code actually uses installed SdFat. Though SdFat is included - but uses different functions and methods.

Not sure if this help to answer - or maybe leads to new questions ...

If the data file is a complete CSV formatted file - like ( comma count may be off ) - where each line end with a '\n'=newline, and there will be 8 lines:
Code:
,,WK,,,,,,
,WP,,,,,,WP,
....WP, BB, QW
WP,,,,,,,
,,,,,,,WP
,,BP,,BB,,BP
BP,,WR,,WR,, BP
BK, BR,,,BP,,,

Each line should account for 8 fields, 7 commas - though trailing commas could be dealt with as not present leaving the space(s) open when empty squares

Have just been looking at SD & SdFat in TD 1.47 there is this example in SdFat library: ...\hardware\teensy\avr\libraries\SdFat\examples\ReadCsvFile\ReadCsvFile.ino
>> The File IO works - but uses SdFat funcs() instead of SD - so don't mix and match { like I did :( in starting }

It has code to Parse a CSV file:
Code:
bool parseLine(char* str) {
  char* ptr;

  // Set strtok start of line.
  str = strtok(str, ",");
  if (!str) return false;
  
  // Print text field.
  Serial.println(str);
  
  // Subsequent calls to strtok expects a null pointer.
  str = strtok(nullptr, ",");
  if (!str) return false;
  
  // Convert string to long integer.
  int32_t i32 = strtol(str, &ptr, 0);
  if (str == ptr || *skipSpace(ptr)) return false;
  Serial.println(i32);
  
  str = strtok(nullptr, ",");
  if (!str) return false;
  
  // strtoul accepts a leading minus with unexpected results.
  if (*skipSpace(str) == '-') return false;
  
  // Convert string to unsigned long integer.
  uint32_t u32 = strtoul(str, &ptr, 0);
  if (str == ptr || *skipSpace(ptr)) return false;
  Serial.println(u32);

  str = strtok(nullptr, ",");
  if (!str) return false;
  
  // Convert string to double.
  double d = strtod(str, &ptr);
  if (str == ptr || *skipSpace(ptr)) return false;
  Serial.println(d);
  
  // Check for extra fields.
  return strtok(nullptr, ",") == nullptr;
}

Looking at that sample the above parseLine() is passed a string for processing.
It would need alteration to recognize the Chess notations, i.e. set the board space not just print.
for instance it needs int countCol=0 that increments each token/comma field to map into the board[Row][countCol]
- and would also needs to know what line# so it can apply the parsing to the right Row.
Code:
parseLineCHESS( char* str, int Row ){
int countCol=0;

// ...
}

PsortaCode:
Code:
char board[8][8]; // global or pass into parseLineCHESS()
for ( int ii=0; ii<8; ii++) // empty the board
 for ( int jj=0; jj<8; jj++)
   board[ii][jj]=0;
open file
for ( int ii=0; ii<8; ii++) {
  read line
  parseLineCHESS( line, ii ); // this would parse each line and mark the occupied space in board[][] as it counts though the 'comma' fields
  // it could simply change 0 to 1 as it goes (to denote LED OFF/ON] or could decode the 6 piece types [P,Ki,Q,R,Kn,B] and differentiate them by color where White [1-6] and Black [11-16]
}

Hello defragster,

I have to say thank you right off the bat for writing all of this for me. Atm I'm using Teensy Loader 1.52 which is higher then what you stated above. I would assume this would also use SdFat. I will have to redo the SD card file to account for this new library. This will take me some time to go through at learn but I will look for that example code and also read and re read your post.

Thank you again defragster, Now I need to go into bubble and figure see if I can start to understand this code !!!


Cheers for now !

Joe
 
OPPS - TYPO on my part See :: pjrc.com/threads/66357-Teensyduino-1-54-Beta-7

1.54 is in beta and the SdFat library and examples are in that release.

All of that code will work with SD.h - just have to use the proper function and variables as is done now.

haha I have been looking for like 20 mins to find these files haha Only found SD.

Ok I will look to download Teensyduino-1-54-Beta and look for the example files there.

Thanks again defragster, Going to try my best to get it working !
 
OPPS - TYPO on my part See :: pjrc.com/threads/66357-Teensyduino-1-54-Beta-7

1.54 is in beta and the SdFat library and examples are in that release.

All of that code will work with SD.h - just have to use the proper function and variables as is done now.

Hey defragster really quickly what did you mean, by just have to use the proper function and variables as is done now.

So what you are saying is I can use the above code with Teensy Loader 1.52 and just have to change some functions that are used with the SD.h library instead of SdFAT.

Also is
Code:
 str = strtok(str, ",")
defined in SdFAT?

Thanks for the response

Joe
 
Last edited:
The Parse code doesn't need changed as it just gets a string.

What would be different would be the file operations using SdFat to open and read the file to get those string from the file - so the code feeding the Parse function would be either using the code in the SdFat example - or like already in use in the current code using SD.h coding functions.

The problem is mixing and matching where the SD.h code works without some of the overhead/options but lesser feature set - but in the end Paul has mapped the SD functions to actually use the SdFat library code for the common features SD.h provided.

>> Didn't mean to confuse the issue - just didn't want the alternate interfaces to bite you like it did me when I did that the other day.

Like
if (!sd.begin(SD_CONFIG)) {
versus:
if (!SD.begin(chipSelect)) {

and
if (!file.open("ReadCsvDemo.csv", FILE_WRITE)) {
versus:
myFile = SD.open("test.txt", FILE_WRITE);
 
The Parse code doesn't need changed as it just gets a string.

What would be different would be the file operations using SdFat to open and read the file to get those string from the file - so the code feeding the Parse function would be either using the code in the SdFat example - or like already in use in the current code using SD.h coding functions.

The problem is mixing and matching where the SD.h code works without some of the overhead/options but lesser feature set - but in the end Paul has mapped the SD functions to actually use the SdFat library code for the common features SD.h provided.

>> Didn't mean to confuse the issue - just didn't want the alternate interfaces to bite you like it did me when I did that the other day.

Like
if (!sd.begin(SD_CONFIG)) {
versus:
if (!SD.begin(chipSelect)) {

and
if (!file.open("ReadCsvDemo.csv", FILE_WRITE)) {
versus:
myFile = SD.open("test.txt", FILE_WRITE);

Well tomorrow is another day, Thank you again defragster, I feel like I own your a case of beer for all this help you are giving me... :)

I will look to use the SD.h library and use the parse code example you laid out. I hope I can get it working tomorrow !!

Cheers for now,
Joe
 
Good luck! Hopefully it does lead to a good start.

I just came across that and figured sharing was easy enough to help your progress.
 
Good luck! Hopefully it does lead to a good start.

I just came across that and figured sharing was easy enough to help your progress.

Hey defragster,

Sadly things are not going great for me, I was able to get the 1.154.0-beta7+sha.ab262c6 to build and work with my old code!. Then it was time to move into the new SdFAT library

Code:
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/teensy/teensy36.html
PLATFORM: Teensy (4.12.0) > Teensy 3.6
HARDWARE: MK66FX1M0 180MHz, 256KB RAM, 1MB Flash
DEBUG: Current (jlink) External (jlink)
PACKAGES:
 - framework-arduinoteensy 1.154.0-beta7+sha.ab262c6
 - toolchain-gccarmnoneeabi 1.50401.190816 (5.4.1)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 96 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <RTClib> 1.12.4
|   |-- <Wire> 1.0
|-- <U8g2> 2.28.8
|   |-- <SPI> 1.0
|   |-- <Wire> 1.0
|-- <Wire> 1.0
|-- <SdFat> 2.0.5-beta.1
|   |-- <SPI> 1.0
Building in release mode

I opened the file that you pointed to , I saw the code you were referring to. but I wasn't able to get the code to work. So I decided to try to just print the files in the sd card from the new library "SdFat.h" I copied everything from ReadCsvFile.ino and wasn't able to get it to compile.

Code:
#include "SdFat.h"

// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
#define SD_FAT_TYPE 0
/*
  Change the value of SD_CS_PIN if you are using SPI and
  your hardware does not use the default value, SS.  
  Common values are:
  Arduino Ethernet shield: pin 4
  Sparkfun SD shield: pin 8
  Adafruit SD shields and modules: pin 10
*/

// SDCARD_SS_PIN is defined for the built-in SD on some boards.
#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SS;
#else  // SDCARD_SS_PIN
// Assume built-in SD is used.
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif  // SDCARD_SS_PIN

// Try to select the best SD card configuration.
#if HAS_SDIO_CLASS
#define SD_CONFIG SdioConfig(FIFO_SDIO)
#elif ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
#else  // HAS_SDIO_CLASS
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
#endif  // HAS_SDIO_CLASS

#if SD_FAT_TYPE == 0
SdFat sd;
File file;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 file;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile file;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile file;
#else  // SD_FAT_TYPE
#error Invalid SD_FAT_TYPE
#endif  // SD_FAT_TYPE

char line[40];

//------------------------------------------------------------------------------
// Store error strings in flash to save RAM.
#define error(s) sd.errorHalt(&Serial, F(s))
//------------------------------------------------------------------------------
// Check for extra characters in field or find minus sign.
char* skipSpace(char* str) {
  while (isspace(*str)) str++;
  return str;
}
//------------------------------------------------------------------------------
bool parseLine(char* str) {
  char* ptr;

  // Set strtok start of line.
  str = strtok(str, ",");
  if (!str) return false;
  
  // Print text field.
  Serial.println(str);
  
  // Subsequent calls to strtok expects a null pointer.
  str = strtok(nullptr, ",");
  if (!str) return false;
  
  // Convert string to long integer.
  int32_t i32 = strtol(str, &ptr, 0);
  if (str == ptr || *skipSpace(ptr)) return false;
  Serial.println(i32);
  
  str = strtok(nullptr, ",");
  if (!str) return false;
  
  // strtoul accepts a leading minus with unexpected results.
  if (*skipSpace(str) == '-') return false;
  
  // Convert string to unsigned long integer.
  uint32_t u32 = strtoul(str, &ptr, 0);
  if (str == ptr || *skipSpace(ptr)) return false;
  Serial.println(u32);

  str = strtok(nullptr, ",");
  if (!str) return false;
  
  // Convert string to double.
  double d = strtod(str, &ptr);
  if (str == ptr || *skipSpace(ptr)) return false;
  Serial.println(d);
  
  // Check for extra fields.
  return strtok(nullptr, ",") == nullptr;
}
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  
  // Wait for USB Serial 
  while (!Serial) {
    yield();
  }
  Serial.println("Type any character to start");
  while (!Serial.available()) {
    yield();
  }
  // Initialize the SD.
  if (!sd.begin(SD_CONFIG)) {
    sd.initErrorHalt(&Serial);
    return;
  }
  // Remove any existing file.
  if (sd.exists("ReadCsvDemo.csv")) {
    sd.remove("ReadCsvDemo.csv"); 
  }
  // Create the file.
  if (!file.open("ReadCsvDemo.csv", FILE_WRITE)) {
    error("open failed");
  }
  // Write test data.
  file.print(F(
    "abc,123,456,7.89\r\n"
    "def,-321,654,-9.87\r\n"
    "ghi,333,0xff,5.55"));
    
  // Rewind file for read.
  file.rewind();
  
  while (file.available()) {
    int n = file.fgets(line, sizeof(line));
    if (n <= 0) {
      error("fgets failed"); 
    }
    if (line[n-1] != '\n' && n == (sizeof(line) - 1)) {
      error("line too long");
    }
    if (!parseLine(line)) {
      error("parseLine failed");
    }
    Serial.println();
  }
  file.close();
  Serial.println(F("Done"));
}


I think this might be a library issue but it doesn't seem to be able to define file I get the follow three error messages.
'File' does not name a type
'file' was not declared in this scope
'file' was not declared in this scope

Isn't this line File file; defining File?

Also note that I'm using platform.io in VSC

Thanks in advanced
Joe
 
Back
Top