Index = 1, always

Pulling my hair out here.

This script is supposed to read a file of words. Different lengths. I only want 8 character words. Discard anything else. Remove CR and/or LF

  • Store 100 passwords in an multidimensional array... password[100][9]

After many hours I get the LCD to say Index = 1 (around line 110).. If I remove "index = 0" a little further down, it does not work at all. Index = never appears on the LCD.

Perhaps someone can look at this and see the problem right away. Right now I am frustrated.

Thanks for the help.

/* Using this for debug messages */
#include <SoftwareSerial.h>

/* Ethernet shield SD Card Reader */
#include <SPI.h>
#include <SD.h>

/* LCD Screen */
#include <Wire.h>
#include <LiquidCrystal_I2C.h>


#define DEBUGSERIALTXPIN    8
#define DEBUGSERIALRXPIN    -1
#define SDCHIPSELECT        9
#define FILENAME            "passwords1.txt"
#define NUMBEROFPASSWORDS   100
#define PASSWORDLENGTH      8

/* GLOBALS */
unsigned long LineNumber = 0;
char          password[NUMBEROFPASSWORDS][PASSWORDLENGTH] = {""};


// I2C LCD
LiquidCrystal_I2C lcd(0x27, 20, 2); // Set LCD address to 0x27 and 20 chars wide by 2 rows tall


/* ***********************
   ***      SETUP      ***
   *********************** */
void setup()
{
  char tmp;
  int  x;

  // Disable the Ethernet SPI
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);

  // Serial ports
  Serial.begin(19200);
  Serial1.begin(19200);
  Serial2.begin(19200);
  Serial3.begin(19200);

  // Software Serial for Debug messages
  SoftwareSerial debugSerial(DEBUGSERIALRXPIN, DEBUGSERIALTXPIN); // RX, TX
  debugSerial.begin(9600);
  debugSerial.print("Software Serial TX ready on pin = ");
  debugSerial.println(DEBUGSERIALTXPIN);

  // LCD Init
  lcd.init();
  lcd.backlight();
  lcd.setCursor(1, 0);
  lcd.print("LCD Initialized");
  lcd.setCursor(1, 1);


  // Initialize SD Card
  if (!SD.begin(SDCHIPSELECT))
  {
    lcd.clear();
    lcd.print("SD Card Error!");
    debugSerial.println("initialization failed.");
    while (true);
  }  // End of IF
}  // End of Setup




/* **********************
   ***      LOOP      ***
   ********************** */
void loop()
{
  int CurrentPasswordNumber;
  char str[80] = "";   // Assume a word will not be more than 80 chars
  byte index = 0;
  char nextChar;

  File myFile = SD.open(FILENAME);

  while (myFile.available() > 0)    // File has data
  {
      while ( CurrentPasswordNumber < NUMBEROFPASSWORDS)
      {
       nextChar = myFile.read();
       while (nextChar != '\r' && nextChar != '\n') {
              nextChar = myFile.read();
              if (nextChar != '\r' && nextChar != '\n')
              {                                            // nextChar is NOT a CR or LF..
                  str[index++] = nextChar;                 // So add nextChar to str[index] (increment index after)
                  str[index] = '\0';                       // Keep last element terminated
              }
              else
              {  // The else executes if nextChar IS a CR or LF...
                  nextChar = myFile.peek();                // Peek at next char to see if it too is a CR or LF
                  if ((nextChar == '\n') || (nextChar == '\r'))  
                  {
                      nextChar = myFile.read();            // Discard the next character in myFile.Read()
                  }
              }
          
              lcd.clear();
              lcd.print("index = ");
              lcd.setCursor(1,1);
              lcd.print(index);    // INDEX IS ALWAYS 1
              if (index == PASSWORDLENGTH)
              {
                  password[CurrentPasswordNumber][0] = str;
                  lcd.clear();
                  lcd.print("Added.  x = ");  lcd.print(CurrentPasswordNumber);
                  delay(3000);
                  CurrentPasswordNumber++;
              }
              index = 0;   // Removing this line seems to cause havoc
              LineNumber++;
      
          } // End of while to get next char
  
      }  // X Number of passwords while

  }  // The end of file hit
  myFile.close();  // Reached end of file
}

// EOF

Your loop to read the next character always sets index at 0 so when you read a character that is not the end marker you go to 1 (and then back to 0).

You need to structure the code so that you have a while loop to read a line in which you increase index

The statement

is within an if, which is within an if, which is within a while, which is within a while, which is within a while, which is within void loop().

And the result is:

and

which doesn't surprise me.

If you write procedures (which you test as you proceed) and use some indirection, the problem will probably clear itself for you, or at least become much more manageable.

consider

#define FILENAME            "passwords1.txt"
#define NUMBEROFPASSWORDS   80
#define PASSWORDLENGTH      8

char          password [NUMBEROFPASSWORDS][PASSWORDLENGTH] = {};
int           nPassword = 0;

int
readLine (
    File  myFile,
    char *s,
    int   size )
{
    int n = 0;
    while (myFile.available () > 0)  {
        char c = myFile.read ();
        if (c != '\r')
            s [n++] = c;
        if (size <= n || '\n' == c)  {
            s [n-1] = 0;
            break;
        }
    }
    return n;
}

// -------------------------------------
void
dispPasswords (void)
{
    for (unsigned n = 0; n < nPassword; n++)  {
        char s [20];
        sprintf (s, " %2d %s", n, & password [n][0]);

        lcd.clear ();
        lcd.setCursor (1,1);
        lcd.print (s);

        delay (3000);
    }
}

// -------------------------------------
void
readPasswords (void)
{
    File myFile = SD.open (FILENAME);

    char str [80];

    while (readLine (myFile, str, sizeof (str)))  {
        char *p = & password [nPassword++][0];
        strncpy (p, str, PASSWORDLENGTH);
    }
}

// -----------------------------------------------------------------------------
void
loop (void)
{
    readPasswords ();
    dispPasswords ();
}

Thank you @gcjr
This mostly works!

If I knew what this was doing or how to deal with it, please can someone explain?

    while (readLine (myFile, str, sizeof (str)))
    {
        char *p = & password [nPassword++][0];
        strncpy (p, str, PASSWORDLENGTH);   // destination, source...  seems like it should be str, p
    }

This appears to copy anything to the "password" array and cuts it at the length??
We need to add to the password array only if it is 8 characters. If it is more than 8 or less than 8, ignore it and move on to the next one in the file.

Working on figuring it out.

Thank you!

Also, the script seems to read the same words over and over. The same "NUMBEROFPASSWORDS", it does not move on to the next set.

you can add a test to see of the strlen() of str is 8

isn't NUMBEROFPASSWORDS the max size of the array? nPassword is the number of passwords read

NUMBEROFPASSWORDS is the max size of the array.
I wanted to do it in batches of 100 passwords.
The file can be a GB or 2..

So, load 100 passwords, try them, come back for next 100.

What is happening,
100 passwords load and display.
loop
same 100 passwords and display

Eventually it freezes. hehe

I guess I could just do one password at a time. No sense in loading 100 into an array.

is nPassword being reset ??

is the same file being reopened each time?

Same file. We never close the file, actually.

Let me repost the code, maybe I broke it.

...

#define DEBUGSERIALTXPIN    8
#define DEBUGSERIALRXPIN    -1
#define SDCHIPSELECT        9
#define FILENAME            "Passwords1.txt"
#define NUMBEROFPASSWORDS   100
#define PASSWORDLENGTH      8

/* GLOBALS */
unsigned long LineNumber = 0;
char          password [NUMBEROFPASSWORDS][PASSWORDLENGTH] = {};
int           nPassword = 0;

// I2C LCD
LiquidCrystal_I2C lcd(0x27, 16, 2); // Set LCD address to 0x27 and 20 chars wide by 2 rows tall


void setup()
{
  // Disable the Ethernet SPI
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);

  // Serial ports
  Serial.begin(19200);
  Serial1.begin(19200);
  Serial2.begin(19200);
  Serial3.begin(19200);

  // Software Serial for Debug messages
  SoftwareSerial debugSerial(DEBUGSERIALRXPIN, DEBUGSERIALTXPIN); // RX, TX
  debugSerial.begin(9600);
  debugSerial.print("Software Serial TX ready on pin = ");
  debugSerial.println(DEBUGSERIALTXPIN);

  // LCD Init
  lcd.init();
  lcd.backlight();
  lcd.setCursor(1, 0);
  lcd.print("LCD Initialized");
  lcd.setCursor(1, 1);


  // Initialize SD Card
  if (!SD.begin(SDCHIPSELECT))
  {
    lcd.clear();
    lcd.print("SD Card Error!");
    debugSerial.println("initialization failed. Things to check:");
    debugSerial.println("1. is a card inserted?");
    debugSerial.println("2. is your wiring correct?");
    debugSerial.println("3. did you change the chipSelect pin to match your shield or module?");
    debugSerial.println("Note: press reset or reopen this serial monitor after fixing your issue!");
    while (true);
  }  // End of IF
}



void loop() {
    readPasswords ();
    dispPasswords ();
}



int readLine (File myFile, char *s, int size )
{
    int n = 0;

    while (myFile.available () > 0)
    {
        char c = myFile.read ();
        if (c != '\r')
            s [n++] = c;
        if (size <= n || '\n' == c)
        {
            s [n-1] = 0;
            break;
        }
    }
    return n;
}

// -------------------------------------
void dispPasswords (void)
{
    for (unsigned n = 0; n < nPassword; n++)
    {
        char s [20];
        sprintf (s, " %2d %s", n, & password [n][0]);

        lcd.clear ();
        lcd.setCursor (0,1);
        lcd.print (s);

        delay (100);
    }
}

// -------------------------------------
void readPasswords (void)
{
    File myFile = SD.open (FILENAME);

    char str [80];
    int  currentLoop = 0;

    nPassword = 0;

    while (readLine (myFile, str, sizeof (str)))
    {
        char *p = & password [nPassword++][0];
        strncpy (p, str, PASSWORDLENGTH);   // destination, source

        currentLoop++;
        if (currentLoop == NUMBEROFPASSWORDS)
        {
              currentLoop = 0;
              break;
        }
    }
}

the open should be done once, probably in setup(). make myFile global.

good!

It has been running for about 10 minutes and has not frozen, ignore the freezing.

Making File global...

/* GLOBALS */
unsigned long LineNumber = 0;
char          password [NUMBEROFPASSWORDS][PASSWORDLENGTH] = {};
int           nPassword = 0;
File          myFile    = SD.open (FILENAME);

....

// -------------------------------------
void readPasswords (void)
{
//    File myFile = SD.open (FILENAME);

Nothing happens. I mean the LCD says "LCD Initialized".. that's it. I ran into this last night and wound up putting everything in loop() which led to the spaghetti!! We need to call File after SD.Init and stuff (which is done in setup). So I put File between void setup() {} and void loop() {} .. which made no diff haha

So, let's use unsigned long linenumber and myFile.seek() ?

oh no, that won't work, seek works on bytes and not lines. aarrgh

Guess it is one pass at a time.

This seems to work!!!!

/* GLOBALS */
uint64_t      charnumber = 0;
int readLine (File myFile, char *s, int size )
{
    int n = 0;

    while (myFile.available () > 0)
    {
        char c = myFile.read ();
        charnumber++;    // Count the chars
        if (c != '\r')
            s [n++] = c;
        if (size <= n || '\n' == c)
        {
            s [n-1] = 0;
            break;
        }
    }
    return n;
}

// -------------------------------------
void readPasswords (void)
{
    File myFile = SD.open (FILENAME);

    char str [80];
    int  currentLoop = 0;

    nPassword = 0;

    if ( charnumber > 0 ) {   // Skip to where we left off!!!!!!
      myFile.seek(charnumber);
    }

    while (readLine (myFile, str, sizeof (str)))
    {
        char *p = & password [nPassword++][0];
        strncpy (p, str, PASSWORDLENGTH);   // destination, source

        currentLoop++;
        if (currentLoop == NUMBEROFPASSWORDS)
        {
              currentLoop = 0;
              break;
        }
    }
}