Program freeze (SRAM problems?)

My program 2-20s after loading and I don’t know why. Maybe it’s a SRAM overusage but I don’t know how to optimize my code.

Edit: The 2nd version of the code (the final version, the first wasn’t finised yet) crash when loading data from the file.

GreenOS4.ino (12.1 KB)

GreenOS4.ino (13.7 KB)

that's a lot of code, but I noticed this:

String InputValue;

avoiding String class is strongly encouraged.

I'd start there.

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. Just use cstrings - char arrays terminated with 0.

...R

BulldogLowell:
that's a lot of code, but I noticed this:

String InputValue;

avoiding String class is strongly encouraged.

I'd start there.

If I replace String by char*, "InputValue" become messed up, the script run normally but the result are a bunch of giberrish.

STUDIOCRAFTapps:
If I replace String by char*, "InputValue" become messed up, the script run normally but the result are a bunch of giberrish.

did you also modify all of the corresponding code related to parsing that String value?

post what you did...

Change Log:

  1. Replace “String InputValue” by “char* InputValue

  2. Asing “InputValue” to “stringconv” (aka: String Converison Variable) instead of converting “InputValue” to a char array (char name not char* name).

  3. Replace “InputValue.length()” by “sizeof(InputValue)-1

[Downloadable file below]

GreenOS4.ino (13.7 KB)

in C++ you use strlen() to find the length of a (NULL terminated) string.

char myString[64] = "Test";

int length = sizeof(myString); // returns 64
length = strlen(myString);  // returns 4

BulldogLowell:
in C++ you use strlen() to find the length of a (NULL terminated) string.

char myString[64] = "Test";

int length = sizeof(myString); // returns 64
length = strlen(myString);  // returns 4

The program is still not functionning

STUDIOCRAFTapps:
The program is still not functionning

sorry, the code you are working with suggested you had more experience.

char* InputValue;  // that is a pointer to a char
char InputValue[64] = "";  // this is a 64 byte char array suitable for using a a buffer of up to 63 bytes

you have to learn how to parse information into a char array...

Google Arduino Serial Input Basics

BulldogLowell:
sorry, the code you are working with suggested you had more experience.

I'm a C# developper, that's why I have problem with how String, char*, char and array in general work on Arduino

You’ve been told not to use String before:

Jobi-Wan:
You can save some by not using Strings.

There are many reasons we are telling you this. You have experienced this common timeline of String usage. If you really want to know why, read all this (especially The Evils of Arduino Strings). You don’t have to understand this advanced information to stop using String.

Here are some examples to get you started. Instead of this:

  for(byte i = 0; i < TimeTypeCount; i++) {
    //OPENING THE FILE
    File CV = SD.open("/SF" + String(i) + ".DAT");
    
    //READING CONTENT
    while (CV.available()) {
      if(char(CV.read()) == '\n') {
        WritingCursor[i]++;
      }
    }
  }

… do this:

  char filename[] = "/SF-.DAT";
  for(byte i = 0; i < TimeTypeCount; i++) {
    //OPENING THE FILE
    filename[3] = i + '0';  // replace the '-' with a digit
    File CV = SD.open( filename );
    
    //READING CONTENT
    while (CV.available()) {
      if(char(CV.read()) == '\n') {
        WritingCursor[i]++;
      }
    }
  }

Instead of these two functions:

void GetValues () {
  if(Serial.available()) {
    InputValue = "";
    while(Serial.available()) { //mySerial.available()
      InputValue = (InputValue + char(Serial.read())); //mySerial.read()
    }
    
    stringconv = new char[InputValue.length()];
    InputValue.toCharArray(stringconv,InputValue.length());
    
    counter2 = 0;
    
    Value[0] = Analizing(0,InputValue.length(),',').toFloat();
    Value[1] = Analizing(counter2,InputValue.length(),',').toFloat();
    Value[2] = Analizing(counter2,InputValue.length(),',').toFloat();
    Value[3] = Analizing(counter2,InputValue.length(),'\n').toFloat();
  }
}

String Analizing (short Initializer, short Lenght, char Character) {
  String Value = "";
  for(short x = Initializer; x < Lenght; x++) {
    if(stringconv[x] == Character) {
      counter2=x+1;
      break;
    }
    Value = (Value+stringconv[x]);
  }
  return Value;
}

… just do this:

bool GetValues () {

  bool lineReceived = false;

  while(Serial.available()) { //mySerial.available()
    char c = Serial.read();

    if (c == '\n') { // newline means the line is all here!
      lineReceived = true;
      InputValue[ InputCount ] = '\0'; // NUL-terminate the C string

      // Parse the line
      char *token = strtok( InputValue, ',' );
      Value[0] = atof( token );
      token = strtok( NULL, ',' ); // get the next token
      Value[1] = atof( token );
      token = strtok( NULL, ',' );
      Value[2] = atof( token );
      token = strtok( NULL, ',' );
      Value[3] = atof( token );

      InputCount   = 0; // reset the count

    } else {
      //  Not a newline, save another character (if there's room)
      if (InputCount < sizeof(InputValue)) {
        InputValue[ InputCount++ ] = c;
      }
    }
  }
  
  return lineReceived;
}

GetBytes is based on the “Serial Input Basics” (see Useful Links). It returns true when the complete line has been received.
Instead of waiting 5 seconds and then parsing the line, keep calling GetBytes until it returns true. Then you know a new command was received. loop should use it like this:

  if (GetValues()) {

    if(TCursor0+1 == 12) {
      TCursor0 = 0;
      if(TCursor1+1 == 5) {
          ...
      }
    }
  }
} // loop

Then you don’t need current/previous millis.

You don’t even need “Analizing”. The strtok and atof functions do that for you, using a char array (aka C string). There is a C string version of every String function you are using. If you’re not sure how to convert a String to a C string, just ask. There are usually several ways to do the same thing. Tutorials here, here and here.

When all the String variables are gone, you program will be much smaller, faster and more reliable.

Cheers,
/dev

-dev:
You've been told not to use String before:There are many reasons we are telling you this. You have experienced this common timeline of String usage. If you really want to know why, read all this (especially The Evils of Arduino Strings). You don't have to understand this advanced information to stop using String.
son9.html]here[/url] and here.

Before starting recoding all of my program, I need to promise you that I'll not to use String until I know how to use them correcly. (And I'll also stop creating new topics for the same problem over and over)

My program is now running without freezing! But the values read are wrong for some reason, for exemple: Values[0] return 0.00 all the time.

STUDIOCRAFTapps:
My program is now running without freezing! But the values read are wrong for some reason, for exemple: Values[0] return 0.00 all the time.

maybe the way you are parsing…

what does the send data look like? you can check:

  InputValue[ InputCount ] = '\0'; // NUL-terminate the C string
  Serial.println(InputValue); // <<<<<<<here
  // Parse the line
  char *token = strtok( InputValue, ',' );

BulldogLowell:
maybe the way you are parsing…

what does the send data look like? you can check:

  InputValue[ InputCount ] = '\0'; // NUL-terminate the C string

Serial.println(InputValue); // <<<<<<<here
  // Parse the line
  char *token = strtok( InputValue, ‘,’ );

It returns “\n”

In case you have not found it. Serial Input Basics - simple reliable ways to receive data. There is also a parse example.

...R

BulldogLowell:
maybe the way you are parsing…

what does the send data look like? you can check:

  InputValue[ InputCount ] = '\0'; // NUL-terminate the C string

Serial.println(InputValue); // <<<<<<<here
  // Parse the line
  char *token = strtok( InputValue, ‘,’ );

The problem come from these line:

InputCount++;
InputValue[InputCount] = c;

Edit: InputCount is not normal:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
0
0
0
0
0
0
0
0
0
0
0

STUDIOCRAFTapps:
The problem come from these line:

InputCount++;

InputValue[InputCount] = c;

The example provided is a good start, but I prefer a more OO/functional approach.

Here is a way to parse using your array filling:

const size_t MAX_MESSAGE_LENGTH = 32;

//tested using 3.14, 4.25, 8.14, 3.77

void setup() 
{
  Serial.begin(9600);
  Serial.println("let's go!");
}

void loop() 
{
  if(char* newMessage = checkForNewMessage(Serial, '\n'))
  {
    float myArray[4];
    Serial.println(newMessage);
    strtok(newMessage, ",");
    myArray[0] = atof(newMessage);
    myArray[1] = atof(strtok(NULL, ","));
    myArray[2] = atof(strtok(NULL, ","));
    myArray[3] = atof(strtok(NULL, ","));
    for(auto i : myArray)
      Serial.println(i);
  }
}

char* checkForNewMessage(Stream& stream, const char endMarker)
{
  static char incomingMessage[MAX_MESSAGE_LENGTH] = "";
  static byte idx = 0;
  if(stream.available())
  {
    incomingMessage[idx] = stream.read();
    if(incomingMessage[idx] == endMarker)
    {
      incomingMessage[idx] = '\0';
      idx = 0;
      return incomingMessage;
    }
    else
    {
      idx++;
      if(idx > MAX_MESSAGE_LENGTH - 1)
      {
        stream.println(F("{\"error\":\"message too long\"}"));  //you can send an error to sender here
        idx = 0;
        incomingMessage[idx] = '\0';
      }
    }
  }
  return NULL;
}

BulldogLowell:
The example provided is a good start, but I prefer a more OO/functional approach.

Here is a way to parse using your array filling:

const size_t MAX_MESSAGE_LENGTH = 32;

//tested using 3.14, 4.25, 8.14, 3.77

void setup()
{
  Serial.begin(9600);
  Serial.println("let's go!");
}

void loop()
{
  if(char* newMessage = checkForNewMessage(Serial, '\n'))
  {
    float myArray[4];
    Serial.println(newMessage);
    strtok(newMessage, ",");
    myArray[0] = atof(newMessage);
    myArray[1] = atof(strtok(NULL, ","));
    myArray[2] = atof(strtok(NULL, ","));
    myArray[3] = atof(strtok(NULL, ","));
    for(auto i : myArray)
      Serial.println(i);
  }
}

char* checkForNewMessage(Stream& stream, const char endMarker)
{
  static char incomingMessage[MAX_MESSAGE_LENGTH] = "";
  static byte idx = 0;
  if(stream.available())
  {
    incomingMessage[idx] = stream.read();
    if(incomingMessage[idx] == endMarker)
    {
      incomingMessage[idx] = '\0';
      idx = 0;
      return incomingMessage;
    }
    else
    {
      idx++;
      if(idx > MAX_MESSAGE_LENGTH - 1)
      {
        stream.println(F("{"error":"message too long"}"));  //you can send an error to sender here
        idx = 0;
        incomingMessage[idx] = '\0';
      }
    }
  }
  return NULL;
}

Your method looks a lot easier (And it also support SD because you're using Stream)
I'll try it now and I'll see if it works

BulldogLowell:
The example provided is a good start, but I prefer a more OO/functional approach.

Here is a way to parse using your array filling:

const size_t MAX_MESSAGE_LENGTH = 32;

//tested using 3.14, 4.25, 8.14, 3.77

void setup()
{
  Serial.begin(9600);
  Serial.println(“let’s go!”);
}

void loop()
{
  if(char* newMessage = checkForNewMessage(Serial, ‘\n’))
  {
    float myArray[4];
    Serial.println(newMessage);
    strtok(newMessage, “,”);
    myArray[0] = atof(newMessage);
    myArray[1] = atof(strtok(NULL, “,”));
    myArray[2] = atof(strtok(NULL, “,”));
    myArray[3] = atof(strtok(NULL, “,”));
    for(auto i : myArray)
      Serial.println(i);
  }
}

char* checkForNewMessage(Stream& stream, const char endMarker)
{
  static char incomingMessage[MAX_MESSAGE_LENGTH] = “”;
  static byte idx = 0;
  if(stream.available())
  {
    incomingMessage[idx] = stream.read();
    if(incomingMessage[idx] == endMarker)
    {
      incomingMessage[idx] = ‘\0’;
      idx = 0;
      return incomingMessage;
    }
    else
    {
      idx++;
      if(idx > MAX_MESSAGE_LENGTH - 1)
      {
        stream.println(F("{“error”:“message too long”}"));  //you can send an error to sender here
        idx = 0;
        incomingMessage[idx] = ‘\0’;
      }
    }
  }
  return NULL;
}

THANKS! IT WORKS! After few month of fixs and tweaks, my program works!