Scope issue when converting fgets example to function

Hello,

I am trying to write a function that will return the next unread line of a text file saved on an SD card. I have modified the fgets example below to read my ‘dutycycle.txt’ file. All of the lines in the file are the same length.

// Demo of fgets function to read lines from a file.
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"

// SD chip select pin
//#define CS_PIN 53
const uint8_t chipSelect = 53;

SdFat sd;
// print stream
ArduinoOutStream cout(Serial);

//------------------------------------------------------------------------------
// store error strings in flash memory
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
char getLights() {
  char line[33];
  // open  file
  SdFile rdfile("dutycycle.txt", O_RDONLY);

  // check for open error
  if (!rdfile.isOpen()) {
    error("demoFgets");
  }
  // read line of rdfile to line with 33 char (32+ line return)
  rdfile.fgets(line, sizeof(line));
  cout << line << endl;
}

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

  // Wait for USB Serial
  while (!Serial) {
    SysCall::yield();
  }

  cout << F("Type any character to start\n");
  while (!Serial.available()) {
    SysCall::yield();
  }
  delay(400);  // catch Due reset problem

  // Initialize at the highest speed supported by the board that is
  // not over 50 MHz. Try a lower speed if SPI errors occur.
  if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
    sd.initErrorHalt();
  }

  //---
  // When fgets() is put into function form need to open file using SdFile, the prints the first line 3 times
  getLights();
  getLights();
  getLights();

  cout << F("\nDone\n");
}
void loop(void) {}

The problem is that this code re-reads this first line; returning this to the serial monitor.

Type any character to start
005,005,005,005,005,005,005,005

005,005,005,005,005,005,005,005

005,005,005,005,005,005,005,005

Done

I figured the issue was opening the file within the function. However, when I move the SdFile line outside of the function I get an open error.

The modified script is

// Demo of fgets function to read lines from a file.
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"

// SD chip select pin
//#define CS_PIN 53
const uint8_t chipSelect = 53;

SdFat sd;
// print stream
ArduinoOutStream cout(Serial);
//moved opening the file to the preamble instead of within the getLights function
SdFile rdfile("dutycycle.txt", O_RDONLY);

//------------------------------------------------------------------------------
// store error strings in flash memory
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
char getLights() {
  char line[33];
  // open  file
  //SdFile rdfile("dutycycle.txt", O_RDONLY);

  // check for open error
  if (!rdfile.isOpen()) {
    error("demoFgets");
  }
  // read line of rdfile to line with 33 char (32+ line return)
  rdfile.fgets(line, sizeof(line));
  cout << line << endl;
}

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

  // Wait for USB Serial
  while (!Serial) {
    SysCall::yield();
  }

  cout << F("Type any character to start\n");
  while (!Serial.available()) {
    SysCall::yield();
  }
  delay(400);  // catch Due reset problem

  // Initialize at the highest speed supported by the board that is
  // not over 50 MHz. Try a lower speed if SPI errors occur.
  if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
    sd.initErrorHalt();
  }

  //---
  // When fgets() is put into function form need to open file using SdFile, the prints the first line 3 times
  getLights();
  getLights();
  getLights();

  cout << F("\nDone\n");
}
void loop(void) {}

Returns

Type any character to start
error: demoFgets

What am I missing?

Alternatively, I’d be happy if I could supply the function with the file location as a multiple of the line character length and the needed line number. All lines in the dutycycle.txt file are the same length.

I’ve also tried calling fgets in the setup multiple times, and the code behaves as I’d like returning the next file line.

dutycycle.txt (97 Bytes)

// read line of rdfile to line with 33 char (32+ line return)

If the comment is accurate you did not leave space for the string terminator.

Furthermore, on some systems (I don't know if this is one of them) the line terminator (or line return) is actually two characters.

char getLights() {
  char line[33];
  // open  file
  //SdFile rdfile("dutycycle.txt", O_RDONLY);

  // check for open error
  if (!rdfile.isOpen()) {
    error("demoFgets");
  }
  // read line of rdfile to line with 33 char (32+ line return)
  rdfile.fgets(line, sizeof(line));
  cout << line << endl;
}

You lied. You promised that this function would return a char. It does not.

  // read line of rdfile to line with 33 char (32+ line return)

The extra space is NOT for the carriage return or line feed. It is for the terminating NULL that ALL strings have.

Thanks PaulS for catching that. I'm a noob with Arduino coding. What is the function returning?

What is the function returning?

You tell me what your return statement tells the function to return.