Help debugging simple atoi function

I am trying to understand how to use atoi. I created a code that used it, but soon found out I didn’t fully understand how it works.

#include <SPI.h>
#include <SD.h>         //SD Card Library
#include <Wire.h>       //I2C Library

int CS_pin = 4;


//--------------New---------------------------------------------
char ExampleFile[] = "file.txt";
const byte numChars = 32;
//char convert[numChars];
int T1M = 0;  // Time1 Minutes
int T1S = 0;  // Time1 Seconds
int T2H = 0;  // Time2 Hours
int T2M = 0;  // Time2 Minutes
int T2S = 0;  // Time2 Seconds

//-----------------------end--------------------------------------------


void setup()
{ //static byte sb = 0;       
  Serial.begin(9600);
  Serial.println("Initializing Card");
  //CS Pin is an output
  pinMode(CS_pin, OUTPUT);

  //Initialize Card
  if (!SD.begin(CS_pin))
  {
    Serial.println("Card Failure");
    return;
  }
  Serial.println("Card Ready");
  if (SD.exists(ExampleFile)) 
    //======================= Get Numbers from SD ================================
  { 
    boolean Time2Read = 1;
    File SdFile = SD.open(ExampleFile);
    if (SdFile)
    { 
      int NumberBracket = 0;
      int NI = 0;
      while (SdFile.available() > 0)
      { 
        char c = SdFile.read();
        if (c != '(') 
        { 
          if (c == ')')
          { 
            Time2Read = 0;
          }
          if (Time2Read == 1)
          { 
            if (NumberBracket == 1)
            { static byte sb = 0; 
              char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T1M=atoi (convert); // Time1 Minutes
            }
            else if (NumberBracket == 2)
            { 
              static byte sb = 0; 
              char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T1S=atoi (convert); // Time1 Seconds 
            } 
            else if (NumberBracket == 3)
            { 
              static byte sb = 0; 
              char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T2H=atoi (convert);//Time2 Hours
            } 
            else if (NumberBracket == 4)
            { 
              static byte sb = 0; 
              char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T2M=atoi (convert);//Time2 Minutes
            }
            else if (NumberBracket == 5)
            { 
              static byte sb = 0; 
              char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T2S=atoi (convert);//Time2 Seconds
            }
          }
        } 
        else { 
          NumberBracket++;
          Time2Read =1;
        }
      }

        Serial.print("Time1 is ");
        Serial.print(T1M);
        Serial.print(" Minutes ");
        Serial.print(T1S);
        Serial.println(" Seconds");
        Serial.print("Time2 is ");
        Serial.print(T2H);
        Serial.print(" Hours ");
        Serial.print(T2M);
        Serial.print(" Minutes ");
        Serial.print(T2S);
        Serial.println(" Seconds ");
    }
    SdFile.close();                                        
  } 
  else {

//======================================= Where numbers come from in ()'s ==================================================    
    File SdFile = SD.open(ExampleFile, FILE_WRITE); 
    SdFile.print("Time 1: ");
    SdFile.println("()minutes(1)seconds");

    SdFile.print("Time2: ");
    SdFile.println("(3)hours(45)minutes(5)seconds");
    SdFile.close();
  }
}

void loop()
{ 
}

The serial monitor readout for this is in the attachment. Notice everything works properly until it gets to the Seconds in Time2, this is because of the double digit minutes that came before the seconds. If you take the double digit number away everything works.

=========================Random Question==========================
This is a little off topic, but how dlloyd post the picture dirrectly in his post #18? I tried the Insert an image but it says you have to give it an url, so did he save that file to a website first, or did he do it a different way?

in the code above you’ll notice in the setup the commented out //char convert[numChars]; and then in the void setup you’ll notice the commented out //static byte sb=0;

The reason I commented this out was because originally everything worked fine with this setup, but then when I entered a double digit, every digit after that was a double digit. For example, if the digit after a double digit was 4, it would read it as 40. I know in chars you have to tell it how many digits to look for in the ; so I figured it wasn’t resetting, I tried a lot of things, that i’m not going to mention because they obviously didn’t work but I did spend about 6 hours trying to debug it yesterday. In the end I commented them out and set them up in each different bracket as below shows

else if (NumberBracket == 2)
{
static byte sb = 0;
char convert[numChars];

I thought that would surely address the problem. That didn’t work, and now i’m extremely confused because to my understanding, it should reset everything each time it finds a new bracket, in the current set up nothing should carry over…

Can someone point me in the direction to get this working properly?

Readout.jpg

The parse example in serial input basics includes the use of atoi()

See the documentation for the function here

...R

Have you tried printing convert before doing the atoi() ? Do they look right ? are they correctly zero terminated as required by atoi() ?

Since convert can only ever have one char, why not just convert the char to a digit by subtracting '0'. Why does convert need to be an array if it will only ever have one char in it? If you still want to use atoi, you'll need to put a 0 after the char to null terminate the string. atoi expects a null terminated string. But I think it is silly to use atoi to convert a single character.

Robin, the code I am using originally came from your examples you referenced. I read everything multiple times, but something isn’t clicking. I even tried using this example

// simple parse demo
char receivedChars[] = "This is a test, 1234, 45.3" ;

char messageFromPC[32] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;

char recvChar;
char endMarker = '>';
boolean newData = false;


void setup() {
	Serial.begin(9600);
	Serial.println("<Arduino is ready>");
	
	parseData();
	showParsedData();
}


void loop() {

}

	
void parseData() {

    // split the data into its parts
    
  char * strtokIndx; // this is used by strtok() as an index
  
  strtokIndx = strtok(receivedChars,",");      // get the first part - the string
  strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
  
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  integerFromPC = atoi(strtokIndx);     // convert this part to an integer
  
  strtokIndx = strtok(NULL, ","); 
  floatFromPC = atof(strtokIndx);     // convert this part to a float

}


void showParsedData() {
	Serial.print("Message ");
	Serial.println(messageFromPC);
	Serial.print("Integer ");
	Serial.println(integerFromPC);
	Serial.print("Float ");
	Serial.println(floatFromPC);
}

and where it said NULL, and ‘,’ i tried to put a ‘(’ and a ‘)’ but I couldn’t get it to work properly

Since convert can only ever have one char, why not just convert the char to a digit by subtracting ‘0’. Why does convert need to be an array if it will only ever have one char in it? If you still want to use atoi, you’ll need to put a 0 after the char to null terminate the string. atoi expects a null terminated string. But I think it is silly to use atoi to convert a single character.

I need to be able to do math like adding numbers to it. I can’t do that if they are in char form, and it won’t let me use something like

            else if (NumberBracket == 5)
            { static byte sb = 0; 
              char convert[numChars];
              convert[sb] = c-'0';                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              int NewNumber = convert
              Serial.println(convert);

Have you tried printing convert before doing the atoi() ? Do they look right ?

They look the same as they do after atoi, meaning it still prints the 5 off as a 55 before I use atoi().

are they correctly zero terminated as required by atoi() ?

Probably not, I tried using

while (SdFile.available() > 0)
{
char c = SdFile.read();
//Serial.println(c);
if (c != ‘(’)
{
if (c == ‘)’)
{

the ‘)’ as a termination indicator, but are you staying

//======================================= Where numbers come from in ()'s ==================================================    
    File SdFile = SD.open(ExampleFile, FILE_WRITE); 
    SdFile.print("Time 1: ");
    SdFile.println("()minutes(1)seconds");

    SdFile.print("Time2: ");
    SdFile.println("(3)hours(45)minutes(5)seconds");
    SdFile.close();

should be changed to

//======================================= Where numbers come from in ()'s ==================================================    
    File SdFile = SD.open(ExampleFile, FILE_WRITE); 
    SdFile.print("Time 1: ");
    SdFile.println("('0')minutes(1'0')seconds");

    SdFile.print("Time2: ");
    SdFile.println("(3'0')hours(45'0')minutes(5'0')seconds");
    SdFile.close();

?

No, like this.

 else if (NumberBracket == 5)
            { 
            
              T2S= c - '0';
            }

Since convert can only have one character in it, the whole numChars and sb index things are useless. If sb is greater than 0 then you are sticking the recieved character c into convert in the middle of the array. Since you never set anything in front of it, there's no telling what atoi is going to return because it is going to start at the beginning of convert, not in the middle where you just put the char.

Are you trying to parse out multidigit numbers? Then you need to move the declaration of convert. It is going out of scope at the end of each if block. It's not going to retain any numbers that were put in there before.

Since you never set anything in front of it

I put the () there for that, so it would know where to start, and end .. If I need something else could you show me an example?

Are you trying to parse out multidigit numbers?

Yes I'm trying to parse out the numbers that were printed to the sd card

SdFile.print("Time 1: "); SdFile.println("()minutes(1)seconds");

SdFile.print("Time2: "); SdFile.println("(3)hours(45)minutes(5)seconds");

Then you need to move the declaration of convert. It is going out of scope at the end of each if block. It's not going to retain any numbers that were put in there before.

wait... are you staying I should put

int times10 = 1;

up top before the setup and then

else if (NumberBracket == 5) { T2S= T2S+times10 * (c - '0');//Time2 Seconds times10=times10*10; } else { times10=1; }

so it knows where to put it?

if (NumberBracket == 1)
            { static byte sb = 0; 
              char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T1M=atoi (convert); // Time1 Minutes
            }

Take this bit. So you only have one character to add at a time, then you go back around loop() to get another character.

So you hit this block, it creates an array called convert and puts the character in convert[0]. Then it tries to call atoi, and then the block ends. When it ends, convert[] goes out of scope, so it disappears.

Now the next time through you have sb = 1 and a new char c. So you make a new array called convert[] and this time you put the char in convert[1]. But you don't know what is in convert[0], you haven't put anything there. This isn't the same array you had last time, it's gone out of scope and been re-created. SO now you have an array whith who knows what in conver[0] and the char you want in convert[1], and still no null terminator. atoi doesn't have a chance.

Yes I tried to address that last night, heres where I got, but this didn’t work either.

#include <SPI.h>
#include <SD.h>         //SD Card Library
#include <Wire.h>       //I2C Library

int CS_pin = 4;


//--------------New---------------------------------------------
char ExampleFile[] = "fle.txt";
const byte numChars = 32;
char convert[numChars];
int T1M = 0;  // Time1 Minutes
int T1S = 0;  // Time1 Seconds
int T2H = 0;  // Time2 Hours
int T2M = 0;  // Time2 Minutes
int T2S = 0;  // Time2 Seconds

//-----------------------end--------------------------------------------


void setup()
{ static byte sb = 0;       
  Serial.begin(9600);
  Serial.println("Initializing Card");
  //CS Pin is an output
  pinMode(CS_pin, OUTPUT);

  //Initialize Card
  if (!SD.begin(CS_pin))
  {
    Serial.println("Card Failure");
    return;
  }
  Serial.println("Card Ready");
  if (SD.exists(ExampleFile)) 
    //======================= Get Numbers from SD ================================
  { 
    boolean Time2Read = 1;
    File SdFile = SD.open(ExampleFile);
    if (SdFile)
    { 
      int NumberBracket = 0;
      int NI = 0;
      while (SdFile.available() > 0)
      { 
        char c = SdFile.read();
        if (c != '(') 
        { 
          if (c == ')')
          { 
            Time2Read = 0;
          }
          if (Time2Read == 1)
          { 
            if (NumberBracket == 1)
            { //static byte sb = 0; 
              //char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T1M=atoi (convert); // Time1 Minutes
            }
            else if (NumberBracket == 2)
            { 
              //static byte sb = 0; 
              //char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T1S=atoi (convert); // Time1 Seconds 
            } 
            else if (NumberBracket == 3)
            { 
              //static byte sb = 0; 
              //char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T2H=atoi (convert);//Time2 Hours
            } 
            else if (NumberBracket == 4)
            { 
              //static byte sb = 0; 
              //char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T2M=atoi (convert);//Time2 Minutes
            }
            else if (NumberBracket == 5)
            { 
              //static byte sb = 0; 
              //char convert[numChars];
              convert[sb] = c;                
              sb++;
              if (sb>=numChars)
              { 
                sb = numChars - 1;
              } 
              T2S=atoi (convert);//Time2 Seconds
            }
          }
        } 
        else { 
          NumberBracket++;
          Time2Read =1;
          sb = 0;
          char convert[]= "";
        }
      }

        Serial.print("Time1 is ");
        Serial.print(T1M);
        Serial.print(" Minutes ");
        Serial.print(T1S);
        Serial.println(" Seconds");
        Serial.print("Time2 is ");
        Serial.print(T2H);
        Serial.print(" Hours ");
        Serial.print(T2M);
        Serial.print(" Minutes ");
        Serial.print(T2S);
        Serial.println(" Seconds ");
    }
    SdFile.close();                                        
  } 
  else {

//======================================= Where numbers come from in ()'s ==================================================    
    File SdFile = SD.open(ExampleFile, FILE_WRITE); 
    SdFile.print("Time 1: ");
    SdFile.println("()minutes(1)seconds");

    SdFile.print("Time2: ");
    SdFile.println("(3)hours(45)minutes(7)seconds");
    SdFile.close();
  }
}

void loop()
{ 
}

What i did was basically comment out the static byte sb = 0;
and char convert[numChars]; and I put them up top out of all the loops so that’s where they would stay so convert would not go out of scope. The theory was when it reached a ‘)’ it would clear itself here

NumberBracket++;
Time2Read =1;
sb = 0;
char convert= “”;
}
}

Serial.print("Time1 is ");

that way the next bracket section would be fresh. Would that work?

Now I don’t have a null, I need to think about how to put that in. I would prefer to put it in the code rather than on the sd card. Can i put it in the time2 seconds section at the end somehow?

From Reply #5

Robin, the code I am using originally came from your examples you referenced. I read everything multiple times, but something isn't clicking. I even tried using this example

and where it said NULL, and ',' i tried to put a '(' and a ')' but I couldn't get it to work properly

Slow down a bit. I can't keep up with you....

I don't know what you mean by the second quote and I don't know if the code you posted is my original (working) version or your modified non-working version.

If you post the modified version I'm sure we will be able to figure out the problem quickly.

Diagnosis and debugging must be done systematically - not with a scatter-gun.

...R

I think I figured how to do it. I have a question for you Robin, or anyone that might know the answer. I realize strings each up your SRAM very quickly, but would it be possible to use chars like strings? Robins examples show how to take one single character at a time and add it to a char.

I'm curious if you can do something like char1 = char2 somehow? Basically read a single character at a time from a char and put that character in another char? That way you could use a char similar to a string. Would that save any more SRAM than if I used strings?

The reason I would want to do this is so I can ultimately have Serial.println(final char) which will display what I want printed instead of using a bunch of if statements that result in original chars printed, each place in my code that I want to print something. I was using Strings to accomplish this, but Strings turned out to be a very bad idea.

Thomas499: Robins examples show how to take one single character at a time and add it to a char.

My examples use char arrays. A string (small s) is a char array with the value 0 (the char '\0') at the end of the valid text. So the char array might be 20 chars long with just the text 'hello' in the first 6 positions and 0 in the 7th position.

There is a whole host of C functions for dealing with strings see here

...R

PS this is a case where Google C string is not very helpful

I have been reading the information on that link since you posted the documentation link. It isn't very clear on how to read one single character from a Char at a time though.

Are you saying string "with lowercase s" uses the same amount of SRAM memory as char Name[20]; and can be used pretty much the same way?

Thomas499: I have been reading the information on that link since you posted the documentation link. It isn't very clear on how to read one single character from a Char at a time though.

Are you saying string "with lowercase s" uses the same amount of SRAM memory as char Name[20]; and can be used pretty much the same way?

char Name[20] IS a string IF you put a null terminator at the end of the characters.

Thomas499: I have been reading the information on that link since you posted the documentation link. It isn't very clear on how to read one single character from a Char at a time though.

Are you saying string "with lowercase s" uses the same amount of SRAM memory as char Name[20]; and can be used pretty much the same way?

A C string (as opposed to a C++ String object) is a fixed-length char array.

What makes it a string is how it is treated.

A C string contains ASCII coded text with a zero as the terminator.

The array must have space for all of it and your code should somehow never exceed the array. The somehow is what your code has to ensure, don't put more chars in a string than can fit with the zero at the end. More than one zero or junk after the zero is no problem but writing past the end of the array is potential for bugs/crashes.

Use ASCII. Use a terminating zero. Color inside the lines (stay in the array). That's it and you have a string that you can work with using string.h functions.

Personally, most of the time I just address the chars in the array individually except to print(). You can do that too since a C string is just a char array with a terminating zero.

Personally, most of the time I just address the chars in the array individually except to print(). You can do that too since a C string is just a char array with a terminating zero.

I'm trying my best to understand this. If you have an array how to do get the chars from it individually? I know how to put the chars in an array individually, but I don't know how to take them from an array and read them individually. I think if I could do this it would solve my problem.

My examples use char arrays. A string (small s) is a char array with the value 0 (the char '\0') at the end of the valid text. So the char array might be 20 chars long with just the text 'hello' in the first 6 positions and 0 in the 7th position.

Can someone show me a line that from the startup section that uses a string(small s)? I've tried string, StringC, stringC, C string, and many other variations. I read the reference link Robin provided but did not see how to set up a string(small s) the only way I've been able to get the arduino to accept string is to use a String(big S).

Is it possible to put strings, or arrays in something like a f()macro? My problem is not total memory, it is SRAM which the strings seem to eat up. the f()macro fixes this for things like serial.print.... Just a thought I figured might be worth asking.

I'm trying my best to understand this. If you have an array how to do get the chars from it individually? I know how to put the chars in an array individually, but I don't know how to take them from an array and read them individually. I think if I could do this it would solve my problem.

Presumably you put the chars in the array with something like

arrayOfChars[someNumber] = 'X';

It should, therefore, come as no surprise that you can read them back with something like

someChar = arrayOfChars[someNumber];

but did not see how to set up a string(small s)

Try this

char arrayOfChars[] = "A C style string";  //the terminating \0 is automatically added
const byte arrayLength = sizeof(arrayOfChars) / sizeof(arrayOfChars[0]);  //the length includes the terminator

void setup()
{
  Serial.begin(115200);
  Serial.println(arrayOfChars);

  Serial.print("Number of characters in the array : ");
  Serial.println(arrayLength);    //note the length reported includes the terminator
  for (int c = 0; c < arrayLength; c++)  //but this for loop stops short of it
  {
    Serial.print(arrayOfChars[c]);
  }
}

void loop()
{
}

This may help show how to get a string from the Serial monitor object:

void setup() {
  // put your setup code here, to run once:
  char strBuffer[20];
  char oneChar;
  int index;
  int charsRead;

  Serial.begin(9600);
  while (true) {
    if (Serial.available() > 0) {
      charsRead = Serial.readBytesUntil('\n', strBuffer, sizeof(strBuffer) - 1);
      strBuffer[charsRead] = '\0';    // Make it a string
      Serial.println(strBuffer);      // Show the string
      index = 0;
      while (strBuffer[index]) {      // Walk through the string...
        oneChar = strBuffer[index++]; // Assign it to a temp and increment the index
        Serial.print(" ");            // Add a space...
        Serial.print(oneChar);        // Show the char
      }     // End while (strbuffer)
    }       // End if()
  }         // End while (true)
}

void loop() {
  // put your main code here, to run repeatedly:

}

The while (true) expression causes the program to spin around waiting for you to enter a letter in the Serial object's textbox and hit the Enter key. The Serial.available() method sees the first character, but the readBytesUntil() method keeps reading until it finds a newline character ('\n'), or reads 19 characters. The sizeof() operator prevents us from overflowing the memory allocated to strBuffer[]. charsRead holds the number of characters you typed in. For example, if you typed in Hello, charsRead equals 5. We then set strBuffer[5] to null. This converts the stream of characters read from the Serial object into a C string. Notice that Hello occupies elements 0-4 and the last character in the array is now a null. The rest of the code is pretty self-explanatory.

is there a way to get all of this out of the SRAM though? like a f()macro or something?