Micro SD reading a csv file issue

Hey everyone I'm working on a project for my dad to make a mini firework launching module, and I'm trying to get it to read time (ms), Event (fire), and Cue (which is for the Ematch), I have 8 'Cues i can use. For some reason it will not read "Cues" but looks like it reads everything else. I know little to nothing about coding but this is what I have so far.

For the csv. file I'm trying to have it read this format and understand it. I've tried everything I know but cant get it to "load" or "arm" the cues at all. Any help at all would be greatly appreciated, or push in the right direction!

Time (ms),Event,Cue
1000,Fire,1
2000,Fire,2
3000,Fire,3


Serial Monitor
23:15:13.501 -> Initializing SD card...
23:15:13.945 -> SD card ready!
23:15:13.945 -> Reading cues from file...
23:15:13.977 -> Line: Time (ms),E
23:15:14.010 -> Line: 1000,Fire,1
23:15:14.010 -> Line: 2000,Fire,2
23:15:14.041 -> Line: 3000,Fire,3
23:15:14.073 -> Cues loaded: 0
23:15:14.073 -> Waiting for trigger on D9...
23:15:14.106 -> Trigger received! Starting show...
23:15:14.143 -> Show complete.


#include <SPI.h>
#include <SD.h>


const int chipSelect = 10;
const int triggerPin = 9;  // PC817 output connected here
const int muxS[4] = {2, 3, 4, 5};     // Multiplexer control pins S0–S3
const int sigPin = 8;                // Multiplexer SIG pin changed to D8


struct Cue {
  unsigned long timeMs;
  int channel;  // 0–7 for C0–C7
};


#define MAX_CUES 100
Cue cueList[MAX_CUES];
int cueCount = 0;


void setup() {
  Serial.begin(9600);
  delay(1000);  // Ensure Serial is ready
  pinMode(triggerPin, INPUT_PULLUP);


  for (int i = 0; i < 4; i++) {
    pinMode(muxS[i], OUTPUT);
    digitalWrite(muxS[i], LOW);
  }


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


  Serial.println("Initializing SD card...");
  SPI.setClockDivider(SPI_CLOCK_DIV8);


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


  Serial.println("SD card ready!");
  File file = SD.open("show.csv");
  if (!file) {
    Serial.println("Failed to open show.csv");
    return;
  }


  Serial.println("Reading cues from file...");
  while (file.available() && cueCount < MAX_CUES) {
    String line = file.readStringUntil('\n');  //
    line.trim();
    if (line.startsWith("\\xEF\\xBB\\xBF")) {
      line = line.substring(3);  // Strip BOM
    }
    Serial.print("Line: ");
    Serial.println(line);


    if (line.length() == 0 || !isDigit(line[0])) continue;


    int comma1 = line.indexOf(',');
    int comma2 = nthIndexOf(line, ',', 1);
    int comma4 = nthIndexOf(line, ',', 3);
    int comma5 = nthIndexOf(line, ',', 4);


    if (comma1 == -1 || comma2 == -1 || comma4 == -1 || comma5 == -1) continue;


    String eventType = line.substring(comma1 + 1, comma2);
    eventType.trim();


    if (!eventType.equalsIgnoreCase("Fire")) continue;


    unsigned long time = line.substring(0, comma1).toInt();
    int cue = line.substring(comma4 + 1, comma5).toInt();


    if (cue >= 1 && cue <= 8) {
      cueList[cueCount].timeMs = time;
      cueList[cueCount].channel = cue - 1;
      Serial.print("Loaded cue ");
      Serial.print(cue);
      Serial.print(" at ");
      Serial.print(time);
      Serial.println("ms");
      cueCount++;
    }
  }


  file.close();
  Serial.print("Cues loaded: ");
  Serial.println(cueCount);
  Serial.println("Waiting for trigger on D9...");
}


void loop() {
  if (digitalRead(triggerPin) == LOW) {
    Serial.println("Trigger received! Starting show...");
    runShow();
    while (true);
  }
}


void runShow() {
  unsigned long startTime = millis();
  for (int i = 0; i < cueCount; i++) {
    while (millis() - startTime < cueList[i].timeMs) {
      // wait
    }
    fireCue(cueList[i].channel);
  }
  Serial.println("Show complete.");
}


void fireCue(int channel) {
  Serial.print("Firing cue on channel ");
  Serial.println(channel + 1);


  for (int i = 0; i < 4; i++) {
    digitalWrite(muxS[i], (channel >> i) & 1);
  }


  digitalWrite(sigPin, HIGH);
  delay(200);  // pulse duration
  digitalWrite(sigPin, LOW);
}


int nthIndexOf(String str, char c, int n) {
  int pos = -1;
  for (int i = 0; i < n; i++) {
    pos = str.indexOf(c, pos + 1);
    if (pos == -1) return -1;
  }
  return pos;
}

Oh my. So much going on. So many logic errors.

1000,Fire,1

How many commas? 2, correct?

    int comma1 = line.indexOf(',');
    int comma2 = nthIndexOf(line, ',', 1);
    int comma4 = nthIndexOf(line, ',', 3);
    int comma5 = nthIndexOf(line, ',', 4);

How many commas are being checked for? How many were in the line again? What value will comma4 and comma5 always have?

And look at these two lines and figure out what the value of comma1 and comma2 will be, and then ask if that's what was intended.

    int comma1 = line.indexOf(',');
    int comma2 = nthIndexOf(line, ',', 1);

Make a simple test case. No SD card. No CSV file. Find logic errors. Fix them. Backport fixes into original program. Profit. Debugging 101.

int cueCount = 0;

void setup() {
   Serial.begin(115200);
   testLine("Time (ms),Event,Cue");
   testLine("1000,Fire,1");
   testLine("2000,Fire,2");
   testLine("3000,Fire,3");
}

void testLine(String line) {
   Serial.print("Line: ");
   Serial.println(line);

   do {
      if( line.length() == 0 || !isDigit(line[0]) ) {
         if( line.length() == 0 ) {
            Serial.println("zero length line");
         } else {
            Serial.println("no leading digit");
         }
         continue;
      }

      int comma1 = line.indexOf(',');
      int comma2 = nthIndexOf(line, ',', 2);

      if( comma1 == -1 || comma2 == -1 ) {
         Serial.print("Comma1 ");
         Serial.print(comma1);
         Serial.print(" Comma2 ");
         Serial.println(comma2);
         continue;
      }

      String eventType = line.substring(comma1 + 1, comma2);
      eventType.trim();

      if( !eventType.equalsIgnoreCase("Fire") ) {
         Serial.print("eventType=");
         Serial.println(eventType);
         continue;
      }

      unsigned long time = line.substring(0, comma1).toInt();
      int cue = line.substring(comma2 + 1).toInt();

      if( cue >= 1 && cue <= 8 ) {
         Serial.print("Loaded cue ");
         Serial.print(cue);
         Serial.print(" at ");
         Serial.print(time);
         Serial.println("ms");
         cueCount++;
      } else {
         Serial.print("cue=");
         Serial.println(cue);
      }
   } while( false );
}


void loop() {
}

int nthIndexOf(String str, char c, int n) {
   int pos = -1;
   for( int i = 0; i < n; i++ ) {
      pos = str.indexOf(c, pos + 1);
      if( pos == -1 ) return -1;
   }
   return pos;
}

Output:

Line: Time (ms),Event,Cue
no leading digit
Line: 1000,Fire,1
Loaded cue 1 at 1000ms
Line: 2000,Fire,2
Loaded cue 2 at 2000ms
Line: 3000,Fire,3
Loaded cue 3 at 3000ms
1 Like