Pointer Array contain weird data after return from function(solved)

Hi all,

I'm try develop a system which using Arduino Mega to capture data from instrument and show on LCD display.

The data capture from instrument was break into a array of pointers by using strtok() and able print out the result correctly.

However, array was showing some weird data after I moving into another function.

Any advise to resolve this issue? Thank you.

#include <stdio.h>
#include <stdlib.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(12, 13);
int i,j;
String serialRead;
char *message[64];
char *ptr=NULL;
byte index = 0;
void setup()
{
Serial_RS232_Setup();
}

void Serial_RS232_Setup()
{
  // Open USB serial communications
  Serial.begin( 9600 );

  // set the data rate for the SoftwareSerial port
  mySerial.begin( 9600 );
}
//waiting to receive data from instrument
void loop()
{
  if ( mySerial.available() )
  {
    serialRead=mySerial.readString();
    StrtoChar();
    Display_txt();
  }

  if ( Serial.available() )
  {
    mySerial.write(Serial.read() );
  }
}

//Break received data into array of pointers
void StrtoChar()
{
// Define 
String str = serialRead; 

// Length (with one extra character for the null terminator)
int str_len = str.length() + 1; 

// Prepare the character array (the buffer) 
char char_array[str_len];

// Copy it over 
str.toCharArray(char_array, str_len);

     ptr = strtok(char_array, ",");  // delimiter
   while (ptr != NULL)
   {
      message[index] = ptr;
      index++;
      ptr = strtok(NULL, ",");
      Serial.println(index);
      
   }
   for(i=0;i<index;i++)
   {
    Serial.print(i);
    Serial.print("-");
    Serial.println(message[i]);
   }
}

//Print out data
void Display_txt()
{
  Serial.print("from display txt");
   for(i=0;i<index;i++)
   {
    Serial.print(i);
    Serial.print("-");
    Serial.println(message[i]);
   } 
}

those variables are local to the function. The allocated Memory is gone/reused when you exit the function so the pointers point at stale data

Easiest solution is making char_array global.
Best way is to give your function a reference to your char_array where it can store the result as a function argument.
Basically you cannot return an array from a function like you would with an int or float.

consider

char s [80];

#define MaxTok  10
char *toks [MaxTok];
int   vals [MaxTok];

// -----------------------------------------------------------------------------
int
tokenize (
    char       *s,
    const char *sep )
{
    unsigned n = 0;
    toks [n] = strtok (s, sep);
    vals [n] = atoi (toks [n]);

    for (n = 1; (toks [n] = strtok (NULL, sep)); n++)
        vals [n] = atoi (toks [n]);

    return n;
}

// -----------------------------------------------------------------------------
void dispToks (
    char * toks [])
{
    char s [40];
    for (unsigned n = 0; toks [n]; n++)  {
        sprintf (s, " %6d  %s", vals [n], toks [n]);
        Serial.println (s);
    }
}

// -----------------------------------------------------------------------------
void loop ()
{
    if (Serial.available ())  {
        int n = Serial. readBytesUntil ('\n', s, sizeof(s));
        s [n] = 0;      // terminate string

        tokenize (s, ",");
        dispToks (toks);
    }
}

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

Hi all, thanks for the reply.

Now I able get the correct data after set variable "char_array" become global variable.

@gcjr method also able get the result perfectly.

Attached image below with @gcjr program result.

Noted: Please ignore the 1st number, these are conversion int number from result.

I have 1 question regarding to @gcjr program, could I set "MaxTok" to a larger value(ex: 80-100)?

You can try and see if the compiler complains about memory. Of course you would have to increase the size also of the cString (char s[]) you analyze

If you expect a very long input, you might think about alternative options like parsing and extracting as you go and not memorize the large String

(You’ll have a bug with the previous code if the separator is not found - n would be 80 and s [n] = 0; would overflow)

@J-M-L had state a good point, actually I should consider more about the memory usage & size of cString (char s[] ) instead of my token size.

Fortunately, It's should be fine because maximum data size will not exceed more than 400 char. Also, I don't worry the bug because instrument will always output a separator '\r' to separate each line of data.

The question is more how far is that separator. If the line is too long then the

readBytesUntil ('\n', s, sizeof(s));

Will return without having found the separator.

The maximum data per line will not exceed 400 char with '\r' at end of line.

I was changed the data size with 400 max size, also with '\r' as my separate. I had tried the program with my instrument and it able handle the result well.

char s [400];
int n = Serial. readBytesUntil ('\r', s, sizeof(s));

not exactly sure what problem you're discussing

it looks like readBytesUntil() returns zero if the terminator is not found before reaching the sizeof(s) value. won't the following address that issue?

    if (Serial.available ())  {
        int n = Serial. readBytesUntil ('\n', s, sizeof(s));
        if (0 == n)
            n = sizeof(s)-1;
        s [n] = 0;      // terminate string

but strtok() works fine even when there is no separator which is the typical case when processing the final token which is delimited by a NULL instead of the separator

I don’t think so, the spec states

Serial.readBytesUntil() returns the number of characters read into the buffer. A 0 means that the length parameter <= 0, a time out occurred before any other input, or a termination character was found before any other input.

So you ll get 80 if that was the size of your array.
Ideally you declare the array with one extra byte and read up to sizeof-1

yes, i',m mistaken (and my original code seems correct)

so i'm confused about your comment #6 and #8

You do this

If the LF is not found, readBytesUntil will populate the entire array and return 80 and then you write a null char at index 80 which is the 81st entry and which does not exist, so you overrrun your array (valid indexes are from 0 to 79)

right. needs correction above

Yes

Usually (well I would not use read bytes until but that’s another discussion)

const size_t commandLineMaxLength = 80;
char commandLine[commandLineMaxLength+1];  // +1 for the trailing null char

and then use commandLineMaxLength Instead of the cryptic sizeof