Read a newline from sd card when button is pressed?

Hi all,

i am trying to get my arduino to read one line from sd card each time the button is clicked. It is just reading the first line and then stopping. This is the simplified version of my code.

#include <SPI.h>
#include <mySD.h>
unsigned char val;
File myFile;
String buffer;
boolean reeed = true;
int inPin = 7;   // choose the input pin (for a pushbutton)
int val = 0;     // variable for reading the pin status

void setup()
{
  pinMode(inPin, INPUT);    // declare pushbutton as input
  
  Serial.begin(115200);
  Serial.print("Initializing SD card...");
  pinMode(SS, OUTPUT);

  if (!SD.begin(13, 15, 2, 14)) { //TTGO ESP32 W/ BUILT-IN SD V1.0
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  // re-open the file for reading:
val = digitalRead(inPin);  // read input value
  if (val == HIGH) { 
   reeed = false; 
  } else {
    reeed =true;
  }

  myFile = SD.open("cmds.txt"); //must be less that 8 characters in filename plus 3 char file extension
  if (myFile) {
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
   if (reeed == true) {
          buffer = myFile.readStringUntil('\n');
          int pix = (buffer.toInt());
          Serial.println(pix);
          reeed = false;
        }
        }
      }
    
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening directions.txt");
  }
}
void loop()
{
  ;
}

Yes, you put your code into the setup function. SO it only runs once. It checks the button once. It prints and then it hits the end of the function. If you want to be able to keep reading, then you will need to do most of that stuff in the loop function which repeats over and over.

I would suggest that you have a look at some of the basic tutorials for the Arduino and learn a little bit about how a sketch works.

Delta_G:
Yes, you put your code into the setup function. SO it only runs once. It checks the button once. It prints and then it hits the end of the function. If you want to be able to keep reading, then you will need to do most of that stuff in the loop function which repeats over and over.

I would suggest that you have a look at some of the basic tutorials for the Arduino and learn a little bit about how a sketch works.

I implemented your suggestion of moving the whole thing to loop, but that didn’t fix the problem on my larger script with more features.

#include <ESP32_Servo.h>
#include <SPI.h>
#include <mySD.h>
Servo printservo;
unsigned char val;
byte lastState = LOW;
int count = 0;
File myFile;
String buffer;
int pix = 0;
int hallz = 0;
boolean reeed = true;
int magnet = 0;
int lastmagnet = 0;
float filter = hallRead();

void setup()
{
  delay(10000);
  printservo.attach(18);  // attaches the servo
  printservo.write(50); //3 degrees default
  pinMode(5, INPUT); // start or halt switch.
  pinMode(1, OUTPUT); //drive motor
  digitalWrite(19, LOW); //no rolling away!
  delay(15);
  pinMode(SS, OUTPUT);
  Serial.begin(115200);
  printservo.write(10);
  Serial.print("Initializing SD card...");
  pinMode(SS, OUTPUT);

  if (!SD.begin(13, 15, 2, 14)) { //TTGO ESP32 W/ BUILT-IN SD V1.0
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  // re-open the file for reading:

  myFile = SD.open("cmds.txt"); //must be less that 8 characters in filename plus 3 char file extension
  
} 
void loop()
{
 if (myFile) {
    // read from the file until there's nothing else in it:
    while (myFile.available()) {

      while (count < 49950) {
        
        int hallz = hallRead();
       filter = filter * 0.9 + hallz * 0.1;

        if (reeed == true) {
          buffer = myFile.readStringUntil('\n');
          int pix = (buffer.toInt());
          Serial.println(pix);
          reeed = false;
        }

       

        if (filter < 25) {
          magnet = 1;

        }
        if (filter > 42 ) {
       magnet = 0;
        }
        
        if (magnet != lastmagnet) {
          count = count + 1;
          Serial.println(filter);
          Serial.println("incremented position");
          lastmagnet = magnet;
        }

        while (count > 2000) {
          if (count == pix ) {
            printservo.write(90);
            delay(15);
            printservo.write(50);
            delay(15);
            reeed = true;
          }
        }
      }
     
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening directions.txt");
  }
}

I wouldn't expect that to do much. You don't set reeed to true anywhere but in the infinite loop at the end. Once count gets greater than 2000 then you go into that while loop and reeed might get set to true (if count == pix) but then nothing in there changes count so you'll never get out of that while loop because count will continue to be greater than 2000.

Why would count not increment? The boolean "reeed" is set to true initially at the top above the setup and loop. Do values not get passed to other loops? It should simply read the first line of cmds.txt, then one next line from cmds.txt every time pix == count. (magnet increments count)

while (count > 2000) {
          if (count == pix ) {
            printservo.write(90);
            delay(15);
            printservo.write(50);
            delay(15);
            reeed = true;
          }
        }

infinite loop here: once you enter this loop, you will never get out of it, because of what @Delta_G said.

while (count < 49950) {
        
        if (magnet != lastmagnet) {
          count = count + 1;
          Serial.println(filter);
          Serial.println("incremented position");
          lastmagnet = magnet;
        }

        ...
      }

possibility of infinite loop here: if “magnet” never changes, then “count” will stuck

while (myFile.available()) {

        ...

        if (reeed == true) {
          buffer = myFile.readStringUntil('\n');
          int pix = (buffer.toInt());
          Serial.println(pix);
          reeed = false;
        }
        ...

        
     
    }

infinite loop here: you only read one line, you will get stuck if the file contains more than one line.

arduino_new:

while (count > 2000) {

if (count == pix ) {
            printservo.write(90);
            delay(15);
            printservo.write(50);
            delay(15);
            reeed = true;
          }
        }



infinite loop here: once you enter this loop, you will never get out of it, because of what @Delta_G said.



while (count < 49950) {
       
        if (magnet != lastmagnet) {
          count = count + 1;
          Serial.println(filter);
          Serial.println(“incremented position”);
          lastmagnet = magnet;
        }


      }



possibility of infinite loop here: if "magnet" never changes, then "count" will stuck



while (myFile.available()) {

if (reeed == true) {
          buffer = myFile.readStringUntil(’\n’);
          int pix = (buffer.toInt());
          Serial.println(pix);
          reeed = false;
        }
        …

}



infinite loop here: you only read one line, you will get stuck if the file contains more than one line.

Count is correct to be dependant on magnet changes. I don’t understand how to prevent it from becoming stuck since the file contains many lines. Is there a way to only execute those loops when count is between 2000 and 49950?

Yes. Use if statements instead of while statements and let the loop function do your looping.

I'm sorry but I don't understand. I changed all ifs to whiles except the "while myfile is available" and it didn't change anything. It still only reads the very first line of sd card text.

Well I’m sure it isn’t as simple as just replacing the word while with the word if. Think about the logic and flow of the program.

Think simple, it should look like this:

void loop()
{
   if (button_is_clicked()) {
      if (myFile.available()) {
         String line = myFile.readStringUntil('\n');
         Serial.println(line);
         //do more things with line here
      } else {
         //handle button is clicked but we reach the end of the file here
      }
   }
}

Warning: String is bad, don't use them.

arduino_new:
Warning: String is bad, don't use them.

So why you suggest it?

Delta_G:
So why you suggest it?

cause I'm lazy

Nope. Now it’s not reading from the sd card at all.

#include <ESP32_Servo.h>
#include <SPI.h>
#include <mySD.h>
Servo printservo;
unsigned char val;
byte lastState = LOW;
int count = 0;
File myFile;
String buffer;
int pix = 0;
int hallz = 0;
boolean reeed = true;
int magnet = 0;
int lastmagnet = 0;
float filter = hallRead();

void setup()
{
  delay(10000);
  printservo.attach(18);  // attaches the servo
  printservo.write(50); //3 degrees default
  pinMode(5, INPUT); // start or halt switch.
  pinMode(1, OUTPUT); //drive motor
  digitalWrite(19, LOW); //no rolling away!
  delay(15);
  pinMode(SS, OUTPUT);
  Serial.begin(115200);
  printservo.write(10);
  Serial.print("Initializing SD card...");
  pinMode(SS, OUTPUT);

  if (!SD.begin(13, 15, 2, 14)) { //TTGO ESP32 W/ BUILT-IN SD V1.0
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  // re-open the file for reading:

  myFile = SD.open("cmds.txt"); //must be less that 8 characters in filename plus 3 char file extension
  
} 

void loop()
{
 int hallz = hallRead();
 filter = filter * 0.9 + hallz * 0.1;
if (filter < 25) {
          magnet = 1;
          }
        if (filter > 42 ) {
       magnet = 0;
        }
        if (magnet != lastmagnet) {
          count = count + 1;
          Serial.println(filter);
          Serial.println("incremented position");
          lastmagnet = magnet;
        }

        if (count < 49950 && count > 2000) {
        if (myFile) {
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      //digitalWrite(19, HIGH);
           if (reeed == true) {
          buffer = myFile.readStringUntil('\n');
          int pix = (buffer.toInt());
          Serial.println(pix);
          reeed = false;
          if (count == pix ) {
            Serial.println("pixel printed");
            printservo.write(90);
            delay(15);
            reeed = true;
          }
          else {
            printservo.write(50);
          }
        }
        
        }
        myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening directions.txt");
  }
}
}

Can you please format that so it is readable. When you see real coders writing, ever notice how all the braces and code blocks line up nice and neat? We don’t do that just to make it pretty. It makes it so that you don’t have to hunt for the little braces to figure out the flow.

You can just press control-T in the IDE and it will do all of that for you. It is effortless, but it makes it much easier on those trying to help you and on you yourself to see what your code does. With the IDE doing it for you, there really is no excuse to have code wandering all over the page like that.

#include <ESP32_Servo.h>
#include <SPI.h>
#include <mySD.h>
Servo printservo;
unsigned char val;
byte lastState = LOW;
int count = 0;
File myFile;
String buffer;
int pix = 0;
int hallz = 0;
boolean reeed = true;
int magnet = 0;
int lastmagnet = 0;
float filter = hallRead();

void setup()
{
  delay(10000);
  printservo.attach(18);  // attaches the servo
  printservo.write(50); //3 degrees default
  pinMode(5, INPUT); // start or halt switch.
  pinMode(1, OUTPUT); //drive motor
  digitalWrite(19, LOW); //no rolling away!
  delay(15);
  pinMode(SS, OUTPUT);
  Serial.begin(115200);
  printservo.write(10);
  Serial.print("Initializing SD card...");
  pinMode(SS, OUTPUT);

  if (!SD.begin(13, 15, 2, 14)) { //TTGO ESP32 W/ BUILT-IN SD V1.0
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  // re-open the file for reading:

  myFile = SD.open("cmds.txt"); //must be less that 8 characters in filename plus 3 char file extension

}

void loop()
{
  int hallz = hallRead();
  filter = filter * 0.9 + hallz * 0.1;
  if (filter < 25) {
    magnet = 1;
  }
  if (filter > 42 ) {
    magnet = 0;
  }
  if (magnet != lastmagnet) {
    count = count + 1;
    Serial.println(filter);
    Serial.println("incremented position");
    lastmagnet = magnet;
  }

  if (count < 49950 && count > 2000) {
    if (myFile) {
      // read from the file until there's nothing else in it:
      while (myFile.available()) {
        //digitalWrite(19, HIGH);
        if (reeed == true) {
          buffer = myFile.readStringUntil('\n');
          int pix = (buffer.toInt());
          Serial.println(pix);
          reeed = false;
          if (count == pix ) {
            Serial.println("pixel printed");
            printservo.write(90);
            delay(15);
            reeed = true;
          }
          else {
            printservo.write(50);
          }
        }

      }
      myFile.close();
    } else {
      // if the file didn't open, print an error:
      Serial.println("error opening directions.txt");
    }
  }
}

See how much easier that is to follow?

Now lets talk about a few things.

int count = 0;

count is an int. What is the largest number an int can hold. If you don’t know then check the reference area on this site.

Knowing that, does this line make any sense?

if (count < 49950 && count > 2000) {

Is the first part even necessary? I think count will always and forever be less than 49950. So there’s no need to test that it is less than 49950. You can guarantee that. It will wrap around to negative values before it ever gets that high.

Have you tried printing count to make sure it holds the number you think it does? Are you sure it is getting larger than 2000?

Where is the hallRead() function. Can you please post the COMPLETE code. At least give me something I can compile and test. Can you imagine how hard it is to fix something when you can’t see all of it?

Hi Delta_G,

First of all, thank you for your patience and effort. I appreciate that.

Since the Arduino compatible board I am using with Arduino IDE is the ESP32, it has different limits for ints.

This code works to print a large int:

void setup() {
  Serial.begin(9600);
  int count = 49950;
  Serial.print("count = ");
  Serial.println(count);
}
void loop() {

}

See data type limits reference if interested: Arduino ESP32 Limits - Github

hallRead(); is a builtin function which doesn’t require a library and it comes from ESP32 Microcontroller’s Espressif firmware that the Arduino IDE accesses through that call. It returns the analog value of the hall effect sensor built in to the ESP32 chip.

For example:

void setup() {
  Serial.begin(115200);
}
 
void loop() {
 
    int measurement = 0;
 
    measurement = hallRead();
 
    Serial.print("Hall sensor measurement: ");
    Serial.println(measurement); 
 
    delay(1000);
}

Here is the complete code as requested It doesn’t read the next line of sd card text when the count increments, but it ought to.:

#include <ESP32_Servo.h>
#include <SPI.h>
#include <mySD.h>
Servo printservo;
unsigned char val;
byte lastState = LOW;
int count = 0;
File myFile;
String buffer;
int pix = 0;
int hallz = 0;
boolean reeed = true;
int magnet = 0;
int lastmagnet = 0;
float filter = hallRead();

void setup()
{
  delay(10000);
  printservo.attach(18);  // attaches the servo
  printservo.write(50); //3 degrees default
  pinMode(5, INPUT); // start or halt switch.
  pinMode(1, OUTPUT); //drive motor
  digitalWrite(19, LOW); //no rolling away!
  delay(15);
  pinMode(SS, OUTPUT);
  Serial.begin(115200);
  printservo.write(10);
  Serial.print("Initializing SD card...");
  pinMode(SS, OUTPUT);

  if (!SD.begin(13, 15, 2, 14)) { //TTGO ESP32 W/ BUILT-IN SD V1.0
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  // re-open the file for reading:

  myFile = SD.open("cmds.txt"); //must be less that 8 characters in filename plus 3 char file extension
  if (myFile) {
    // read from the file until there's nothing else in it:
    while (myFile.available()) {

      while (count < 49950) {
        //all pixels in 300 x 300 square.
        int hallz = hallRead();
        filter = filter * 0.9 + hallz * 0.1;

        if (reeed == true) {
          buffer = myFile.readStringUntil('\n');
          int pix = (buffer.toInt());
          Serial.println(pix);
          reeed = false;
        }

        //Serial.println("rolling");
        digitalWrite(19, HIGH); //drive motor

        if (filter < 25) {
          magnet = 1;

        }
        if (filter > 42 ) {
          magnet = 0;
        }

        if (magnet != lastmagnet) {
          count = count + 1;
          //Serial.print("Magnet = ");
          //Serial.print(magnet);
          //Serial.print(" HallZ = ");
          Serial.println(filter);
          Serial.println("incremented position");
          lastmagnet = magnet;
        }

        while (count > 2000) {
          if (count == pix ) {
            Serial.println("pixel printed");
            printservo.write(90);
            delay(15);
            printservo.write(50);
            delay(15);
            reeed = true;
          }
        }
      }
      digitalWrite(19, LOW);
      //}
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening directions.txt");
  }
}
//} //
void loop()
{
  ;
  // nothing happens after setup
}

ESP32,

You left that detail out earlier. You're asking total strangers who have never seen your work before to help you. You should be giving us ALL the details.

The code you posted just now has the same problems we've already pointed out. You're getting trapped into an infinite loop here:

while (count > 2000) {
          if (count == pix ) {
            Serial.println("pixel printed");
            printservo.write(90);
            delay(15);
            printservo.write(50);
            delay(15);
            reeed = true;
          }
        }

So reeed is true only for the first line and then it is set to false. The only place it ever gets set to true again is inside that infinite loop.

Have you tried printing count to see if it actually has the value you think it does?

With this code, it hits count1 = 5, and then it incessantly loops reading the cmds.txt. It should wait for the next increment to do so.

#include <ESP32_Servo.h>
#include <SPI.h>
#include <mySD.h>
Servo printservo;
unsigned char val;
byte lastState = LOW;
int count = 0;
File myFile;
String buffer;
int pix = 0;
int hallz = 0;
boolean reeed = true;
int magnet = 0;
int lastmagnet = 0;
float filter = hallRead();
int count1 = 0;

void setup()
{
  delay(10000);
  printservo.attach(18);  // attaches the servo
  printservo.write(50); //3 degrees default
  pinMode(5, INPUT); // start or halt switch.
  pinMode(1, OUTPUT); //drive motor
  digitalWrite(19, LOW); //no rolling away!
  delay(15);
  pinMode(SS, OUTPUT);
  Serial.begin(115200);
  printservo.write(10);
  Serial.print("Initializing SD card...");
  pinMode(SS, OUTPUT);

  if (!SD.begin(13, 15, 2, 14)) { //TTGO ESP32 W/ BUILT-IN SD V1.0
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  // re-open the file for reading:

  myFile = SD.open("cmds.txt"); //must be less that 8 characters in filename plus 3 char file extension
  if (myFile) {
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      int hallz = hallRead();
      filter = filter * 0.9 + hallz * 0.1;
      if (filter < 25) {
        magnet = 1;
      }
      if (filter > 42 ) {
        magnet = 0;
      }

      if (magnet != lastmagnet) {
        count1 = count1 + 1;
        //Serial.print("Magnet = ");
        //Serial.print(magnet);
        //Serial.print(" HallZ = ");
        Serial.println(filter);
        Serial.println("incremented sub start position");
        lastmagnet = magnet;
      }

      while (count1 < 49950 && count1 >  5) {
        //all pixels in 300 x 300 square.
        int hallz = hallRead();
        filter = filter * 0.9 + hallz * 0.1;

        if (reeed == true) {
          buffer = myFile.readStringUntil('\n');
          int pix = (buffer.toInt());
          Serial.println(pix);
          reeed = false;
        }

        //Serial.println("rolling");
        digitalWrite(19, HIGH); //drive motor

        if (filter < 25) {
          magnet = 1;

        }
        if (filter > 42 ) {
          magnet = 0;
        }

        if (magnet != lastmagnet) {
          count = count + count1;
          //Serial.print("Magnet = ");
          //Serial.print(magnet);
          //Serial.print(" HallZ = ");
          Serial.println("count + count 1 should not be doubling");
          Serial.print("count + count 1 =");
          Serial.println(count);
          Serial.println(filter);
          Serial.println("incremented position");
          lastmagnet = magnet;
        }

        if (count == pix ) {
          Serial.println("pixel printed");
          printservo.write(90);
          delay(15);
          printservo.write(50);
          delay(15);
          reeed = true;
        }
      }
      digitalWrite(19, LOW);
      //}
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening directions.txt");
  }
}
//} //
void loop()
{
  ;
  // nothing happens after setup
}

How do you know the value of count1? I don't see where it gets printed or output anywhere.

You know what might be smart? Instead of all this handwaving about this code that is obviously mal-formed. Perhaps it would be advantageous to describe what in the world this is about and what it is supposed to be and what it is supposed to do. Then, perhaps, instead of just finding the obvious errors in your code I might be able to help you figure out a better way to do it.