Pulling data from a text document and outputting functions

That's where we disagree as we know. It's OK though - You are fully entitled to your own opinion.
I believe micro-controllers developers need to understand the fundamentals of c-Strings and it's easy enough for needs such as expressed by OP.

The OP asked for Arduino code, not C code

I've not read that.

What I need to do is find a way to read it

Sure the String class is also a possible option.

Handling an array and a cString is not "premature optimization". It's the built-in option in the language and the recommended approach on micro-controllers and you know it as your were advising against the String class in the past

Both Sparkfun and Adafruit advise against using the Arduino String class

(before watering down your stance from discussions here and your good home work on the String class).

--
There is value in your classes and work, but they come with extra learning curve, memory costs, and are totally non standard. So I don't think they fit the simple requirements here. I think you are a victim of the Law of the instrument (or try to drive newbies to your libraries for whatever reason even when there is no value add?)

it is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail.

it's important to resist the temptation or just be aware of the alternatives. And for that you need to be exposed to the alternatives, then you can make an educated decision.

Microsoft's stance is based on code vulnerabilities issues. We all would expect microsoft developers to know how to handle a buffer but, hey they came up with Vista, so....

Eschewing the ease of use, safety and robustness of Strings (and SafeString) for an unspecified and unquantified 'improvement' from using error prone, fragile, c-strings which demand more programming care and more coding for null handling is the definition of "premature optimization"

you are entitled to your opinion.

Mine is that cStrings are fundamental building blocks of the C and C++ language. There is no optimization in using them.

discussion closed for me.

drmpf:
By the way, what Arduino board are you using?
Arduino Strings are essentially bullet proof on UNO and Mega2560.

I am using the UNO board.

I am using the UNO board.

No problems then using Strings.

cStrings are fundamental building blocks

No reason to use c-strings methods when the language provides better more appropriate functions that do a better job.

Using your argument that "the fundamental building blocks" be used, you should write
string copy as

memcpy (dest, src, strlen (src) + 1);

and string compare as

int result;
unsigned char c1,c2;
 do {
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0') {
       break;
      } 
 } while (c1 == c2);
 result = c1 - c2;

J-M-L:
there is nothing so systemic about c-strings, you are just saying you just need to be a diligent programmer to not face any problem.

Receiving bytes in an array until getting a special byte and being careful not to write past the end of the array is all what it takes in OP’s case. Does not seem out of reach for any programmer.

In a nutshell, for OP’s ask and expectations, there is no need for adding clutter with non standard libraries. What’s built in is already plenty and efficient, memory wise and speed wise.

here is sample code receiving from Serial (and outputting some messages back to Serial) at 115200 bauds

const size_t maxCommandNumber = 50;

const size_t maxCommandSize = 3 * maxCommandNumber; // if commands fit on 2 characters and a comma, this is enough for maxCommandNumber commands
char receivedChars[maxCommandSize + 1]; // +1 for trailing null char required by cStrings
const char endMarker = '\n';

void func_L(const char* s)  {Serial.print(F("in Function ")); Serial.println(s);}
void func_R(const char* s)  {Serial.print(F("in Function ")); Serial.println(s);}
void func_Dp(const char* s) {Serial.print(F("in Function ")); Serial.println(s);}
void func_B2(const char* s) {Serial.print(F("in Function ")); Serial.println(s);}
void func_U2(const char* s) {Serial.print(F("in Function ")); Serial.println(s);}
void func_unknown(const char* s) {Serial.print(F("Unknown commmand: ")); Serial.println(s);}

struct {
 const char* label;
 void (func)(const char);
} keys[] = {
 {"L", func_L},
 {"R", func_R},
 {"D'", func_Dp},
 {"B2", func_B2},
 {"U2", func_U2},
};
const size_t nbKeys = sizeof keys / sizeof keys[0];

bool findAndExecute(const char* s)
{
 bool success = false;
 for (size_t i = 0; i < nbKeys; i++)
   if (!strcmp(s, keys[i].label)) {
     keys[i].func(s);
     success = true;
     break;
   }
 if (!success) func_unknown(s);
 return success;
}

bool commandAvailable()
{
 static size_t commandIndex = 0;
 static bool awaitEndMarkerError = false;

int incomingByte = Serial.read();
 bool commandComplete = false;
 if (incomingByte != -1) { // read returns -1 if there is nothing to read (faster than using available)
   if (awaitEndMarkerError) {
     if (incomingByte == endMarker) {
       awaitEndMarkerError = false;
       commandIndex = 0;
       receivedChars[0] = '\0';  // discard what was there
       commandComplete = true; // if we want to still notify the parser we got a command (here will be empty)
     }
   } else {
     char incomingCharacter = incomingByte; // grab LSB as a char
     if (incomingByte != endMarker) {
       if (commandIndex < maxCommandSize) {
         if (!isspace(incomingCharacter)) { // discard all spaces. could also add a test to only keep A-Z and ' and 2, whatever is legit in the command language
           receivedChars[commandIndex++] = incomingCharacter;
           receivedChars[commandIndex] = '\0'; // always maintain a correct cString
         }
       } else {
         Serial.println(F("ERROR: Command line too big. Discarded.\n\t-> increase maxCommandNumber or send shorter command lines."));
         awaitEndMarkerError = true;
       }
     } else {
       commandIndex = 0;
       commandComplete = true;
     }
   }
 }
 return commandComplete;
}

bool handleCommand()
{
 bool success = true;
 const char* separators = ",";
 Serial.print(F("\nparsing : ")); Serial.println(receivedChars);
 char* ptr = strtok(receivedChars, separators);
 if (*ptr == '\0') Serial.println(F("Error: empty command"));
 else
   while (ptr) {
     success &= findAndExecute(ptr);
     ptr = strtok(NULL, separators);
   }
 return success;
}

void setup() {
 Serial.begin(115200);
}

void loop() {
 if (commandAvailable())
   if (! handleCommand())
     Serial.println(F("Some commands were not recognized"));
}




The receiving code discards all spaces in command line. 

you would need to define one function for each command in the language and add the mapping into the keys[] array

The `func_unknown()` function is called if an unknown command is found.

In this code when the buffer is full, you get a warning message and I ignore that command line - basically submit it empty to the parser so that the main code would still know there was an incoming command but it was ill-formed. You could devise a different strategy like handling only what you got (but that comes with a risk if the last digit you got was 'L' and the command was really L2 or L' --> you'd execute something that was not intended.

I could do something in MATLAB to add something like Z to the end of the array, then if it identifies z, it would stop. Or does the /n work just fine?

'\n' works just fine and is not likely to be part of the name of a function. So I'd say it's actually better. it's also user friendly, if you print the commands they will be each on a separate line.

@drmpf, see my post #20, I said "Sure the String class is also a possible option". My view is that it's not needed. OP's needs are straightforward and easily achieved with cStrings. feel free to provide code with the String class. I will not comment on you pathetic unhelpful remark (or is that a comment ? :)) )

PS: @bigolstussy -> no need to quote full text when you answer, just quote the relevant part or just answer without any quote. it makes reading the forum easier

Just using reductio ad absurdum to counter your argument for using c-string methods when there is a better way.

come on @drmpf. - you are better than this...

I said "cStrings are fundamental building blocks of the C and C++ language".

if you disagree with that statement and want to use "reductio ad absurdum" you need to prove that the opposite "scenario" would lead to absurdity or contradiction....

That's not what you've done, you've just made irrelevant comments about standard C functions that have been around forever - so much that they are part of standard libraries found in every C or C++ implementations - and that I'm not even using in the code I proposed.

How could you even disagree with what I said, since your library is built on top of cstrings and cstdlib...

I'd suggest for the benefit of all:

  • define "better" (You just have expressed an opinion).
  • Provide some code with String class and your library
  • explain why your code is "better" than the code I've provided in this specific case

I'm curious to hear your position since your code will be using cStrings, just hidden away from the developer and that:

  • The String library has quirks diligent programmer might not know about (you've listed some in many of your rants)
  • Your code has its quirks too diligent programmer might not know about

if my code has bugs, at least it's in plain sight. May be I missed some coding checks a diligent programmer would add?

I'll stop there as this discussion is worthless for the OP.
Let's agree that we disagree. It's not important.

"reductio ad absurdum" you need to prove that the opposite "scenario" would lead to absurdity or contradiction...

my definition is
*mode of argumentation or a form of argument in which a proposition is disproven by following its implications logically to an absurd conclusion.... *

  • define "better" (You just have expressed an opinion).
  • Provide some code with String class and your library
  • explain why your code is "better" than the code I've provided in this specific case

"better" means easier to use, less to worry about, less chance of fatal coding errors. In this case no null termination code and no precise char[] sizes to check for.
#10 provides the String code
#2 provides similar examples using SafeString

For this example, the String code happily handles larger input lines then were coded for.

You code does at least print an error, but does not actually run successfully and can not be 'fixed' without re-coding.
The String code just works correctly with longer lines. No re-coding needed. You can, optionally, use my StringReserveCheck class to get a warning about processing a longer line.

if my code has bugs, at least it's in plain sight.

At the moment for this short bit of code, yes, but not necessarily true in general. See the examples in my Taming Arduino Strings tutorial, where a small sketch has a char[] /strcpy error, but that error does not show up at all on an UNO, only gives an empty string on ESP8266, but finally crashes on an ESP32. It is well know that lots of buffer overflow problems do not show up in coding/testing. Hence all the successful hacks based on them.

Both Strings and SafeStrings provide 'better' results. Strings just works on all boards and SafeString completely avoids the buffer overflow, does not crash and flags the error on all boards

"better" means easier to use, less to worry about, less chance of fatal coding errors. In this case no null termination code and no precise char[] sizes to check for.

#10 provides the String code

So let’s check:
#10 is as easy to use as my code, isn’t it ?
#10 worries about string size. (Reserve 120, checks for overflow)
#10 actually can grow to 121 as you add a trailing comma, possibly leading to a full buffer space move (once) and thus the need of the buffer size in free SRAM.
#10 has a bug and will fail silently user intent if I send 121 chars with the last ones being ",L1" ‘ so there is worry (fatal coding error - easy to fix if you drop the input as I did)

You might call that early optimization in my code but
#10 is using much more flash memory, a scarce resource on UNO to worry about.
#10 will use way more dynamic memory (the substring part), a scarce resource on UNO, possibly leading to hard to debug crash if code grows (add a screen and fonts, ethernet,...)
#10 will be slower, OP asked for speed.

And I don’t think my code crashes, or you have not pointed out so.

Accordingly I would not call that code in #10 "better" based on your definition, but may be it is just my biased view.

Reality is that you always need to handle edge cases in code, that’s where the devil hides, be it a buffer overflow or unexpected input. It requires diligent programmers doing their job right.

At least OP has 2 ideas to work from, that’s what matters.

opps #10 was an early version and does have the bug you found, thanks for pointing that out.
Try the code at the end of this post which handles longer lines as I discussed. Taking out the bounds check actually makes it 'better'

UNO, possibly leading to hard to debug crash if code grows (add a screen and fonts, ethernet,...)

Won't happen on UNO, unless you start using more then 120 bytes of local variables, char[] etc, due to the way memory is allocated. See my Taming Arduino Strings tutorial, What happens when UNO runs out of heap memory

We are no where near running out of Flash or SRAM for this project. Don't do premature optimization for some thing that may never happen.

The OP has yet to quantify how fast is fast. premature optimization again.

I agree way I have written the substring method uses extra SRAM, but not a problem in this sketch. It just seemed to me to be the most straight forward way to code the algorithm. It can be easily recoded to only use a single field's extra space. But why bother when the SRAM is available. Again avoid premature optimization

At least OP has 2 ideas to work from, that's what matters.

*My sentiments exactly. * We have each argued our case and he will do what he likes.
Also thank you for your comments on my approach.

// sample input
// L,R,D',B,U,D,F',R,B',L',B2,R,F2,D2,F,L',U2,L,U2,L,B2,L',B2,L2,U2,L2,R2,U2,B2,U2
// terminated with a NewLine char '\n'  or CR NL
String inputLine;
unsigned int strInput_RESERVE = 120; // an estimate of the input line length no need to be exact

// stopC is the char to stop at
// returns true when get stopC and input contains the input up to then
// else return false
bool readStrUntil(char stopC, String& input) {
  while (Serial.available()) {
    char c = Serial.read();
    if (c == stopC) {
      return true; // input has char upto stopC
    }
    // else
    input += c;
  }
  return false;
}

void runL() {
  Serial.println("run cmd L");
}
void runR() {
  Serial.println("run cmd R");
}
/// etc
void unknownCmd(String& cmd) {
  Serial.print("cmd:"); Serial.print(cmd); Serial.println(" not programmed yet.");
}

void execute(String& cmd) {
  cmd.trim();
  if (cmd.length() == 0) {
    return;
  }
  if (cmd == "L") {
    runL();
  } else if (cmd == "R") {
    runR();
    // . . . etc
  } else {
    unknownCmd(cmd);
  }
}

void parseInput(String& data) { // note the String&
  Serial.print(F("Data :")); Serial.println(data);
  data.trim();
  if (data.length() == 0) {  //no data just return
    return;
  }
  data += ','; // add a trailing , to simplify the logic
  int idx = data.indexOf(',');
  while (idx >= 0) { // not -1 i.e found ,
    String cmd = data.substring(0, idx);
    execute(cmd);
    data = data.substring(idx + 1); // step over the , and remove the cmd just processed
    idx = data.indexOf(',');
  }
}

void setup() {
  Serial.begin(115200);
  for (int i = 10; i > 0; i--) {
    Serial.print(i); Serial.print(' ');
    delay(500);
  }
  Serial.println();
  inputLine.reserve(strInput_RESERVE); // allow some initial space
}

void loop() {
  if (readStrUntil('\n', inputLine)) { // got a line of input
    parseInput(inputLine);
    inputLine = ""; // clear for next input
  }
}

I did mention you could see that as "early optimization". I see that as fair use of built in capabilities and simple straightforward programming. I find

  char* ptr = strtok(dataToScan, ","); // extract first token
  while (ptr) { // while we have a token
    findAndExecute(ptr); // deal with token
    ptr = strtok(NULL, ","); // grab next token if there is one
  }

much easier to grasp and debug than messing around with indexes and substrings

  data += ','; // add a trailing , to simplify the logic
  int idx = data.indexOf(',');
  while (idx >= 0) { // not -1 i.e found ,
    String cmd = data.substring(0, idx);
    execute(cmd);
    data = data.substring(idx + 1); // step over the , and remove the cmd just processed
    idx = data.indexOf(',');
  }

Simpler code using proven builtin libraries (strtok() has been around forever) == less bugs

dealing with input += c; without checking bounds or memory limits (this can fail and won't be caught) is the source of the problems (dynamic allocation poking holes) you've been trying to address with your library, isn't it ?

If memory becomes an issue later on and OP has to back away from String, that will be a lot of work. I tend to think that starting a program with a sound foundation has more mileage especially when it costs nothing in code complexity (there is no complex string handling) to do it that way compared to other approaches. So it's not a "pay now" or "pay more later" situation....

strtok, much easier to grasp

Only because you are familiar with it

On a first call, the function expects a C string as argument for str, whose first character is used as the starting location to scan for tokens. In subsequent calls, the function expects a null pointer . . .
This end of the token is automatically replaced by a null-character, and the beginning of the token is returned by the function. . .
Once the terminating null character of str is found in a call to strtok, all subsequent calls to this function (with a null pointer as the first argument) return a null pointer.

is not particularly obvious, the actual calling process changes between the first and subsequent calls.
The underlying string gets chopped up and becomes un-useable. This is unexpected and not immediately clear from the description (unless you are a low-level C programmer).
Then you will be suggesting the use of strcpy, to keep a copy of the original input, with all the errors that can bring and including the extra memory usage you were complaining about.
You also end up with a NULL pointer in your data string variable. Always a good source of subsequent errors.

dynamic allocation poking holes

In this simple sketch there will be no fragmentation even if the inputLine resizes. For non-trivial sketches see the Taming Strings guidelines.

If memory becomes an issue later on and OP has to back away from String,

No, just follow the guidelines in Taming Arduino Strings and become aware of how much memory you are using and where.
And there is always SafeString to fall back on which provides similar high level functionallity of Strings and which maintains the built in bounds checking and null terminator handling.

Now about bound checking.

without checking bounds or memory limits (this can fail and won't be caught)

receivedChars[commandIndex++] = incomingCharacter;
will discared lines if they are just 1 char too long,
input += c;
handles lines 10 times the expected size when run on a small memory UNO. If you want to check for input lines exceeding 1200 chars then you can. Most won't.

However.. your comment (and your code) does raise the point about missing NL input validation.
Here are two sketches, the first minimal sketch, revised without the substring memory issue you complained about, and no input NL validation. The second, following, checks for missing/corrupted NL terminator.
Note: the test for line length in the second sketch is to pick up a missing NL's and not to protect a fragile char[]'s from overflowing. The exact number to check the length against is not important when using Strings

I would expect most users would be happy with no NL validation sketch for a first pass as it is so simple, but the choice is theirs.

// NO Input NL Line Validation
// sample input
// L,R,D',B,U,D,F',R,B',L',B2,R,F2,D2,F,L',U2,L,U2,L,B2,L',B2,L2,U2,L2,R2,U2,B2,U2
// terminated with a NewLine char '\n'  or CR NL
String inputLine;

// stopC is the char to stop at
// returns true when get stopC and input contains the input up to then
// else return false
bool readStrUntil(char stopC, String& input) {
  while (Serial.available()) {
    char c = Serial.read();
    if (c == stopC) {
      return true;
    }
    // else
    input += c; // good for >1200 char lines on UNO
  }
  return false;
}

void runL() {
  Serial.println("run cmd L");
}
void runR() {
  Serial.println("run cmd R");
}
/// etc
void unknownCmd(String & cmd) {
  Serial.print("cmd:"); Serial.print(cmd); Serial.println(" not programmed yet.");
}

void execute(String & cmd) {
  cmd.trim();
  if (cmd.length() == 0) {
    return;
  }
  if (cmd == "L") {
    runL();
  } else if (cmd == "R") {
    runR();
    // . . . etc
  } else {
    unknownCmd(cmd);
  }
}

void parseInput(String & data) { // note the String&
  Serial.print(F("Data : '")); Serial.print(data); Serial.println("'");
  data.trim(); // handle <CR><NL>
  data += ','; // add a trailing , to simplify the logic
  int endIdx = 0; int startIdx = 0;
  while ((endIdx = data.indexOf(',', startIdx)) >= 0) { // not -1 i.e found ,
    String cmd = data.substring(startIdx, endIdx);
    execute(cmd);
    startIdx = endIdx + 1; // skip the ,
  }
}

void setup() {
  Serial.begin(115200);
  for (int i = 10; i > 0; i--) {
    Serial.print(i); Serial.print(' ');
    delay(500);
  }
  Serial.println();
}

void loop() {
  if (readStrUntil('\n', inputLine)) { // got a line of input
    parseInput(inputLine);
    inputLine = ""; // clear for next input
  }
}

This sketch detects missing NL and processes the data on a timeout.
If the missing NL causes an abnormally long input (as defined by the user) it is ignored and the following input skipped until it stops or a NL found.

// With Input NL Line Validations
// sample input
// L,R,D',B,U,D,F',R,B',L',B2,R,F2,D2,F,L',U2,L,U2,L,B2,L',B2,L2,U2,L2,R2,U2,B2,U2
// terminated with a NewLine char '\n'  or CR NL
#include <millisDelay.h>
// millisDelay in included in the SafeString library available from the library manager
String inputLine;
bool skippingData = false;

millisDelay newLineTimeout; // catch if input line not terminated with NL
const unsigned long NEW_LINE_TIMEOUT_MS = 1000; // 1sec

bool returnTrueIfNotSkippingData(String& input) {
  if (skippingData) {
    skippingData = false;
    Serial.print(F(" Finished skipping Data. Ignoring this last data '"));
    Serial.print(input);
    Serial.println("'");
    input = ""; // skip this data
    return false;
  } else {
    return true; // input has char upto stopC
  }
}

// stopC is the char to stop at
// returns true when get stopC and input contains the input up to then
// else return false
bool readStrUntil(char stopC, String& input) {
  while (Serial.available()) {
    char c = Serial.read();
    newLineTimeout.start(NEW_LINE_TIMEOUT_MS);
    if (c == stopC) {
      newLineTimeout.stop();
      return returnTrueIfNotSkippingData(input);
    }
    // else
    input += c;
    // check for un-terminated input line
    if (input.length() > 200) { // some large value exact size not important, two line together?
      Serial.print(F("Missing or corrupted NL? Ignoring this data:'"));
      Serial.print(input);
      Serial.println("'");
      Serial.println(F(" and skipping data until input stops or next NL received."));
      skippingData = true; // start skipping
      newLineTimeout.stop();
      input = ""; //
    }
  }
  if (newLineTimeout.justFinished()) {
    if (skippingData) {
      Serial.println(F(" Input stopped. "));
    } else {
      Serial.print(F("Input stopped without a NL. Parsing '"));
      Serial.print(input);
      Serial.println("'");
    }
    return returnTrueIfNotSkippingData(input);
  }
  //
  return false;
}

void runL() {
  Serial.println("run cmd L");
}
void runR() {
  Serial.println("run cmd R");
}
/// etc
void unknownCmd(String & cmd) {
  Serial.print("cmd:"); Serial.print(cmd); Serial.println(" not programmed yet.");
}

void execute(String & cmd) {
  cmd.trim();
  if (cmd.length() == 0) {
    return;
  }
  if (cmd == "L") {
    runL();
  } else if (cmd == "R") {
    runR();
    // . . . etc
  } else {
    unknownCmd(cmd);
  }
}

void parseInput(String & data) { // note the String&
  Serial.print(F("Data : '")); Serial.print(data); Serial.println("'");
  data.trim(); // handle <CR><NL>
  data += ','; // add a trailing , to simplify the logic
  int endIdx = 0; int startIdx = 0;
  while ((endIdx = data.indexOf(',', startIdx)) >= 0) { // not -1 i.e found ,
    String cmd = data.substring(startIdx, endIdx);
    execute(cmd);
    startIdx = endIdx + 1; // skip the ,
  }
}

void setup() {
  Serial.begin(115200);
  for (int i = 10; i > 0; i--) {
    Serial.print(i); Serial.print(' ');
    delay(500);
  }
  Serial.println();
}

void loop() {
  if (readStrUntil('\n', inputLine)) { // got a line of input
    parseInput(inputLine);
    inputLine = ""; // clear for next input
  }
}

Adding this input validation adds noticeable more code and most users will just be happy with the basic sketch that handles 1200 chars

If the user wants this input validation then SafeString is a better choice because the SafeStringReader includes all the input timeout and skipping data logic already

Only because you are familiar with it

your arguments are becoming lame...

every C or C++ programmer should be familiar with it. It’s part of every C/C++ Compiler environment I know of and in cstring default capabilities.

The underlying string gets chopped up and becomes un-useable

There is no expressed need to keep the command line around, Over engineering solutions is not good... and what does your original code with data = data.substring(idx + 1);you had to change the code to look for commas to make your point? (use strchr() instead of strtok()would do exactly the same if that was a requirement).

But sure as i said previously a String based solution can work - but is not better for the expressed requirements than a plain cString solution as demonstrated previously, I’d argue it’s actually worse because of speed and memory usage on small microcontroller and thus a technical debt for the future if code needs te expand.

C or C++ programmer should be familiar with it. It's part of every C/C++ Compiler environment

BUT this is an Arduino Forum for Arduino, not a C/C++ forum.

Users don't join here to learn C/C++. They want to learn to use Arduino.
Arduino Language Reference uses Strings, because they are so much more robust and easier to work with.

Users asking a simple question about Serial I/O should not be shunted down into low-level, non-obvious, error prone, c-string methods, with all the extra code and care required for buffer overflow and null terminators, when the Arduino Language Reference Strings simply work.

Some one coming from Matlab, as we have here, may have never used C/C++, and never want to.

If so why do you keep advertising your complicated and detailed blog posts and non arduino libraries to every OP discussing strings? Why did you write those libraries in the first place?

This is a forum for learning. Arduino’s favored programming language is C++. The arduino doc references two ways for handling text, cStrings and the String class.

cStrings and associated functions are at the foundation of the String class, so it can’t hurt to know about those. This helps not only make an educated decision on using or not using them and also get learners ready on how to correctly receive a binary data stream in an array.

As seen in my code above it’s pretty straightforward to use, fast and memory efficient. So my view is that this is where learners should start from.

The other thing learners need to know is to ask themselves how to handle edge cases to become diligent programmers. Regardless of the prowess of underlying classes or functions you need to decide what to do when something goes wrong or adhere to expectations. Testing return values or error codes etc... Making false promises and using deceptive language to say all is safe no worries leads to disappointment or is pedagogicaly poor.

Enough digression here OP has got 2 good options to work from and if s/he followed all the way here would have learnt a few more things about memory and coding.

Bye.

I ended up using J-M-L's method. I am wondering how I verify what the output looks like. I know in matlab it would be simple, just type the variable and it displays it, but I'm not sure how to do it on the arduino. I need to do this because I cannot continue writing my code to turn the stepper motors if I do not know how the output is set up. Thanks!

What do you call « variable » or output ?