Go Down

Topic: Error when SD card contains more than 10 files (Read 577 times) previous topic - next topic

so_sofft

Jan 06, 2020, 10:28 pm Last Edit: Jan 06, 2020, 10:36 pm by so_sofft
Hello :-)

first post here, I'll try to be as clear as possible, forgive me in advance if I'm not precise enough.

My project:
I'm working on a project for an exhibition where Arduino randomly selects .txt files containing small ASCII art from an SD card and sends them to a thermal printer. The function will be trigged by people pressing a button.

What I'm working with:
• Adafruit thermal printer
• Arduino Uno
• Shield and 8GB SD card

So far I've been able to make Arduino select random files from the SD card and print them. My first try was with just 4 .txt files and when run on the serial monitor the code worked fine, displaying the random ASCII art.

However, when I went to add more files into the SD card (I put 20), on the serial monitor a series of random character appeared (I attach a picture). I tried to gradually lower the number of files and it seems that when no more than 10 files are in the card the monitor can still display the actual ASCII art, but as soon as more are stored, the problem represents itself.


I attach my code here, I'm not sure if the issue is within it or has something to do with the hardware.

I hope someone with similar experiences or suggestions can help me! :-)


Thank you!


Code: [Select]

/*  SDlistFiles

  This example shows how print out the files in a directory on a SD card

  The circuit:
   SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4 Adafruit #2971 and Metro/Uno

  created   Nov 2010 by David A. Mellis
  modified 9 Apr 2012 by Tom Igoe
  modified 2 Feb 2014 by Scott Fitzgerald
  modified 12 Apr 2018 by Mike Barela

  This example code is in the public domain.

*/
#include "Adafruit_Thermal.h"
#include <SPI.h>
#include <SD.h>


#include "SoftwareSerial.h"
#define TX_PIN 6 // Arduino transmit  YELLOW WIRE  labeled RX on printer
#define RX_PIN 5 // Arduino receive   GREEN WIRE   labeled TX on printer

SoftwareSerial mySerial(RX_PIN, TX_PIN); // Declare SoftwareSerial obj first
Adafruit_Thermal printer(&mySerial);     // Pass addr to printer constructor


File root;

int run;
int buttonPin;

void setup() {
  Serial.begin(9600);

  pinMode(7, OUTPUT); digitalWrite(7, LOW);

  mySerial.begin(19200);  // Initialize SoftwareSerial
  printer.begin();        // Init printer (same regardless of serial type)

  // Open serial communications and wait for port to open:
  Serial.begin(9600);

  randomSeed(analogRead(0));
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  pinMode(10, OUTPUT);      // set the SS pin as an output (necessary!)
  digitalWrite(10, HIGH);   // but turn off the W5100 chip

  Serial.print("");

  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("");

  root = SD.open("/");

  printDirectory(root, 0);

  // re-open the file for reading:
  root = SD.open(getRandomFilename("/", getFileCount("/")));
  if (root) {
    Serial.println(getRandomFilename("/", getFileCount("/")));


    // read from the file until there's nothing else in it:
    while (root.available()) {
      Serial.write(root.read());
    }
    // close the file:
    root.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }


  Serial.println(getFileCount("/"));

  Serial.println(getRandomFilename("/", getFileCount("/")));


  //
  //   Serial.println(getFileName(root, 0, random(countDirectory(root,0)) ) );


}

void loop() {
  // nothing happens after setup finishes.
}



void printDirectory(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }

    entry.close();
  }
}

String getFileName(File dir, int numTabs, int index)
{

  int counter = 0;

  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }

    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    if (counter == index)  return entry.name();
    Serial.print(entry.name());
    entry.close();

    counter++;
  }
}

int getFileCount( String dir )
{
  File d = SD.open( dir );
  int count_files = 0;
  while ( true )
  {
    File entry =  d.openNextFile();
    if ( !entry )
    {
      // no more files. Let's return the number of files.
      return count_files;
    }
    String file_name = entry.name();  //Get file name so that we can check
    //if it's a duplicate
    if ( file_name.indexOf('~') != 0 )  //Igrnore filenames with a ~. It's a mac thing.
    { //Just don't have file names that have a ~ in them
      count_files++;
    }
  }
}

String getRandomFilename( String dir, int maxIndex )
{
  File d = SD.open( dir );
  int count_files = 0;
  int random_index = random(maxIndex - 1);
  while ( true )
  {
    File entry =  d.openNextFile();
    if ( !entry )
    {
      // no more files. Let's return the number of files.
      //return count_files;
    }
    String file_name = entry.name();  //Get file name so that we can check
    //if it's a duplicate
    if ( file_name.indexOf('~') != 0 )  //Igrnore filenames with a ~. It's a mac thing.
    { //Just don't have file names that have a ~ in them
      if ( count_files == random_index) return file_name;
      count_files++;
    }
  }
}


christop

#1
Jan 07, 2020, 12:43 am Last Edit: Jan 08, 2020, 06:01 pm by christop
I'm guessing this has something to do with files not being closed before opening a new file, leaving too many open files. I'll let others comment about that.

One thing that stood out to me, though, is the process of selecting a file name at random, which scans the directory twice (once to count the number of files and again to select a file name). Here's an algorithm to select a file name randomly while scanning the directory only once:

  • Set n to 1.
  • Set the selected file name to blank (or some sort of invalid value).
  • Read a file name. If there's no more file names to read, return the selected file name.
  • Generate a random number between 0 and n (exclusive) (simply call random(n)).
  • If the random number equals 0, select this file name (save it to a String object, or whatever).
  • Increment n.
  • Go back to step 3.

EDIT: Step 1 should set n to 1, not 0. Silly off-by-one error!

wildbill

You're using openNextFile in four functions. In two of them, you close the file as per the example code. In the other two, you don't which I suspect is a problem. Use of String objects isn't helping either.

so_sofft

I'm guessing this has something to do with files not being closed before opening a new file, leaving too many open files. I'll let others comment about that.

One thing that stood out to me, though, is the process of selecting a file name at random, which scans the directory twice (once to count the number of files and again to select a file name). Here's an algorithm to select a file name randomly while scanning the directory only once:

  • Set n to 0.
  • Set the selected file name to blank (or some sort of invalid value).
  • Read a file name. If there's no more file names to read, return the selected file name.
  • Generate a random number between 0 and n (exclusive) (simply call random(n)).
  • If the random number equals 0, select this file name (save it to a String object, or whatever).
  • Increment n.
  • Go back to step 3.


thanks for your answer. This looks so much more straightforward and I'll definitely try to use it.

so_sofft

You're using openNextFile in four functions. In two of them, you close the file as per the example code. In the other two, you don't which I suspect is a problem. Use of String objects isn't helping either.
That was it! A quick fix, thanks for answering.

christop

I updated step 1 in my post #1 because the random number in step 4 should be generated between 0 and 1 (exclusive), not between 0 and 0 (exclusive). I don't even know what random(0) will do! (Probably something nasty like divide by zero.)

Go Up