Serial Input Processing

Hello comunity :),

I'm currently working on a small experiment to be implemented in a larger project. In this experiment the user writes something in the serial monitor, terminating their text with a semicolon. The program then stores the text in a string type variable, and echoes it back in confirmation.

Easy right?

The Problem: The code that I have doesn't work all the time. It usually does it's job, but occasionally it does not respond, or prints out gibberish from the memory wastelands. For the life of me I can't find what is causing the issue.

Here is the structure of the program:

#include "AdvancedStructures.h"
#include "StringStream.h"

stringStream myStream(300);
dynamicStack_S testS;

void setup(){    
  Serial.begin(9600);
}
void loop(){
  myStream.listen();
  
  if(myStream.available()){
    String command = myStream.read();
    
    Serial.println(command);
  }
}

The advanced structures header is something I threw together to hold linked lists. I use it for a fifo queue in this project. The string stream header contains the actual logic I use for converting the serial data to a string. Here it is:

#include "AdvancedStructures.h"
/*********************************************************************************************************************************/
/*                          STRING STREAM                                */
/*********************************************************************************************************************************/
class stringStream{
  private:
    dynamicQueue_S _inputStream;
    String _currentString;
    
    int _INPUT_TIMEOUT;
    int _inputTimer;
    
  public:
    stringStream(int timeout);
    void listen();
    String read();
    boolean available();
};

/*********************************************************************************************************************************/

stringStream::stringStream(int timeout){
  _INPUT_TIMEOUT = timeout;
  _currentString = "";
}

/* If a byte is available in the serial stream, read it in and add it to the string. If it's a ';' then
   add the string to the queue and reset it. */
void stringStream::listen(){
  if(Serial.available() > 0){
    char current = Serial.read();
    
    if(current == ';'){
      _inputStream.enqueue(_currentString);
      _currentString = "";
    }
    else _currentString += current;
    
    _inputTimer = millis();
  }
  
  /* If there is too much time between the reading of a character and the submission of a string,
     it generally means the user goofed up. Reset the string in this event. */
  if(millis() - _inputTimer > _INPUT_TIMEOUT) _currentString = "";
}

String stringStream::read(){
  return _inputStream.dequeue();
}

boolean stringStream::available(){
  return !_inputStream.isEmpty();
}

The advanced structures header file can be found with the two files I copied above in the google drive folder. Preliminary testing on the classes in the file yielded no errors, so I don't think the problem is there. That being said, it uses malloc quite a bit, which i understand is iffy on the arduino (and I am also quite new to malloc [this is my first malloc program]]). Also, I know I call advanced structures in the header and the main body. I do that because I intend to detach the header and stick it in the libraries directory. Fear not, the #ifndef has been taken care of.

1 more thing: Because of the thing I want to use this for, I need something similar to the queue I'm using. I need to be able to store an arbitrary number of strings, and only discard them after they've been read. At times, strings will need to be stored faster than they are read.

If you guys have any tips on what I'm doing wrong here, I'd love to hear them.

Google Drive Folder

I think you will find that the much simpler code in Serial Input Basics does what you want. Probably the second example will be most suitable.

It is not a good idea to use Strings (capital S) in the small memory of an Arduino. It can cause memory corruption. It is much safer to use strings (small s). These are the string functions

…R

Kaden-Burgart: I need to be able to store an arbitrary number of strings, and only discard them after they've been read. At times, strings will need to be stored faster than they are read.

"arbitrary" will be rather small if you want to hold the strings in memory (depending on the model). Storing the strings on other media (SD card, ..) needs extra hardware and the transfer speed is eventually limited.

You are checking each malloc call for a NULL return value, aren't you? ;)

Thanks for the replies.

@Robin2 Looking at the example you referenced, you are using the same (or very similar) logic that I am. The main difference is that I wrapped it up in a class and added in the queue to act as a buffer. This leads me to believe that it might be a memory problem with strings as you suggested.

I will work on going through my code and optimizing it for minimal RAM usage. Will I need to write my own substring function when I use a char array? I didn't see any in the link you provided.

@Whandall I could certainly see memory issues being the culprit here. As I said in my post, I am very new to using malloc. Do you mean to ask whether I'm checking to see if the malloc request failed? If so, the answer is no. This did not occur to me in my infinite n00bdom. I'll implement it though.

One more thing. Suppose I do replace all my strings with a char[50]. I only expect a small number of my strings to get that long. Considering this, would the String class really be a worse option? I was under the impression that it allocated only as much space as the string demanded.

Kaden-Burgart:
Considering this, would the String class really be a worse option?

The problem with the String class is that it uses memory dynamically and may not always release memory that it has used.

With an array of char you are in complete control.

I reckon you can use a sequence of simpler functions to get the substring() capability. (That’s why I prefer programming in Python - but you can’t do that on an Arduino)

…R

and may not always release memory that it has used.

That was supposedly fixed with a change in the malloc usage a long time ago.

With an array of char[] you are in complete control.

And the memory locations will probably have empty/wasted memory space like holes like Swiss cheese unless the arrays completely fill the allocated space.