Why or how to fix serial data unwanted concatenations

The current string being sent to an ESP32, then to a logic level converter, then to an Arduino Uno is this:

0.31199996948242187

Although, this is the serial monitor:

xCoo: 1999816894531240.4
xCoo: 0.471999816894531240.31199996948242187
xCoo: 0.471999816894531240.31199996948242187
xCoo: 0.471999816894531240.3110.471999816894531240.31199996948242187
xCoo: 0.471999816894531240.31199996948242187
xCoo: 0.471999816894531240.3110.471999816894531240.31199996948242187
xCoo: 0.471999816894531240.31199996948242187
xCoo: 0.471999816894531240.3110.471999816894531240.31199996948242187
xCoo: 0.471999816894531240.31199996948242187
xCoo: 0.471999816894531240.3110.471999816894531240.31199996948242187
xCoo: 0.471999816894531240.31199996948242187

For what it's worth, here is the program:

#include <SoftwareSerial.h>

SoftwareSerial softSerial1(10, 11); // RX, TX

String rawCoo = "";
String xCoo = "";

byte leng = 0;

const byte dirPinX = 4;
const byte stepPinX = 5;
volatile float deltaX = 0;

volatile int x_1, x_2;
volatile int hasRunXA = 0, hasRunXB = 0;
volatile int resetXcount = 0, resetYcount = 0, stepXcount = 0, stepYcount = 0;

void setup()
{
  softSerial1.begin(9600); //Recieve data on pin 10
  Serial.begin(19200);
//  while (!Serial);    // Needed for native USB port only, 
                      // wait for serial port to connect.
  delay(500); // Necessary for clean serial monitor print

  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);  
}

void loop()
{
  if(softSerial1.available() > 0)
  {
    rawCoo = softSerial1.readStringUntil('\n'); // 0.50 to 0.55
    if(rawCoo != "")
      xCoo = rawCoo;
    Serial.print("xCoo: ");
    Serial.print(xCoo);
    Serial.println();
    delay(5000);
  }
}

(The wiring is correct. It functions normally for other programs.)

Any ideas on why the serial data is being jumbled up? and any ideas on how to improve it?

Try clearing your String after use
rawCoo = ""

readStringUntil('\n') blocks try this instead

String readString;
const size_t readString_RESERVE = 100;
// in setup
void setup() {
  .. ..
  readString.reserve(readString_RESERVE);
   ..  . 
}

bool readLine() {
  if (softSerial1.available()) {
    char c = softSerial1.read(); //gets  one byte from serial buffer
    if ((c == '\n') || (c == '\r')) {
      return true; // found line
    }
    // else
    readString += c;
    if (readString.length() >= readString_RESERVE) {
      return true; // a long line
    }
    // else
  }
  return false;
}
void loop() {
  if (readline()) {
    // go a line of input do something
    readString = ""; // for next line 
  }
}

I'm a bit confused. It seems your suggestion is to discard readStringUntil to read (character by character). It is a bit of an improvement...

#include <SoftwareSerial.h>

SoftwareSerial softSerial1(10, 11); // RX, TX

String rawCoo = "";
String xCoo = "";

byte leng = 0;

const byte dirPinX = 4;
const byte stepPinX = 5;
volatile float deltaX = 0;

volatile int x_1, x_2;
volatile int hasRunXA = 0, hasRunXB = 0;
volatile int resetXcount = 0, resetYcount = 0, stepXcount = 0, stepYcount = 0;

String readString1;
const size_t readString_RESERVE = 100;

void setup()
{
  softSerial1.begin(9600); //Recieve data on pin 10
  Serial.begin(19200);
  delay(500); // Necessary for clean serial monitor print
  
  readString1.reserve(readString_RESERVE);

  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);   
}


void loop()
{
  if (readLine()) // go a line of input do something
    readString1 = ""; // for next line

  Serial.print("xCoo: ");
  Serial.print(xCoo);
  Serial.println();
  delay(5000);    
}

bool readLine()
{
  if(softSerial1.available())
  {
    char c = softSerial1.read(); //gets one byte from serial buffer
    if ((c == '\n') || (c == '\r'))
      return true; // found line

    readString1 += c;
    
    if (readString1.length() >= readString_RESERVE)
      return true; // a long line
  }
  xCoo = readString1;
  Serial.print("xCoo: ");
  Serial.print(xCoo);
  Serial.println();  
  x_1 = xCoo.toInt();

  return false;
}

Ok, stop printing inside the readLine method. Otherwise you will just see the char by char.

Wait until readLine returns true. Then in loop() process the line. See the code in my post,
You can print out the whole line by replacing // got a line with Serial.println(readString)

  if (readline()) {
      Serial.println(readString); // got a line of input do something
      if (processLine(readString)) {
          // valid line ...
      } else { 
         Serial.print(F("Invalid input:")); Serial.print(readString);
      }
    readString = ""; // for next line
  }

So add a bool processLine(String &line) method

bool processLine(String &line) {
  // parse and convert line here
  return true; // if valid return true else return false
}

Remember to actually send a '\n' to terminate the input. readStringUntil( ) has a time out.
The code above does not. If you want a non-blocking input timeout
my SafeString library (available from the library manager) has very convenient non-blocking input functions.

Also the String toInt() is a very sloppy conversion and will return 0 for 0.55 (and 0 for "abc" as well)
Do you expect an int or a float
My SafeString library has much better conversion routines, that you can add on

cSFP(sfStr,readString.c_str)); // short for createSafeStringFromCharPtr
int i;
if (sfStr.toInt(i) ) {
  // valid int
} else {
  Serial.print(sfStr); Serial.println(" is not an int");
}

Yes, it's on my to-do list to use the SafeString library.

/*
 
 */
 #include <SoftwareSerial.h>

SoftwareSerial softSerial1(10, 11); // RX, TX

String rawCoo = "";
String xCoo = "";

byte leng = 0;

const byte dirPinX = 4;
const byte stepPinX = 5;
volatile float deltaX = 0;

volatile int x_1, x_2;
volatile int hasRunXA = 0, hasRunXB = 0;
volatile int resetXcount = 0, resetYcount = 0, stepXcount = 0, stepYcount = 0;

String readString1;
const size_t readString_RESERVE = 100;

void setup()
{
  softSerial1.begin(9600); //Recieve data on pin 10
  Serial.begin(19200);
  delay(500); // Necessary for clean serial monitor print
  
  readString1.reserve(readString_RESERVE);

  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);   
}


void loop()
{
  if (readline())
  {
      Serial.println(readString); // got a line of input do something
      if (processLine(readString))
          // valid line ...
      else
         Serial.print(F("Invalid input:")); Serial.print(readString);
      
    readString = ""; // for next line
  }   
}
bool processLine(String &line)
{
  // parse and convert line here
  return true; // if valid return true else return false
}
bool readLine()
{
  if(softSerial1.available())
  {
    char c = softSerial1.read(); //gets one byte from serial buffer
    if ((c == '\n') || (c == '\r'))
      return true; // found line

    readString1 += c;
    
    if (readString1.length() >= readString_RESERVE)
      return true; // a long line
  }
  xCoo = readString1;
//  Serial.print("xCoo: ");
//  Serial.print(xCoo);
//  Serial.println();  
  x_1 = xCoo.toInt();

  return false;
}

'readline' was not declared in this scope

I'll look at this more deeply. I don't get why not just post the whole program. But thanks for pushing me in the right direction.

Your code has a few identifier mis matches (readLine / readline, readString1/readString )
try this

/*

*/
#include <SoftwareSerial.h>
#include <SafeString.h>

SoftwareSerial softSerial1(10, 11); // RX, TX

String rawCoo = "";
String xCoo = "";

byte leng = 0;

const byte dirPinX = 4;
const byte stepPinX = 5;
volatile float deltaX = 0;

volatile int x_1, x_2;
volatile int hasRunXA = 0, hasRunXB = 0;
volatile int resetXcount = 0, resetYcount = 0, stepXcount = 0, stepYcount = 0;

String readString1;
const size_t readString_RESERVE = 100;

void setup()
{
  softSerial1.begin(9600); //Recieve data on pin 10
  Serial.begin(19200);
  delay(500); // Necessary for clean serial monitor print

  readString1.reserve(readString_RESERVE);

  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);
}



void loop()
{
  if (readLine())
  {
    Serial.println(readString1); // got a line of input do something
    if (processLine(readString1)) {
      // valid line ...
    } else {
      Serial.print(F("Invalid input:")); Serial.print(readString1);
    }

    readString1 = ""; // for next line
  }
}
bool processLine(String &line)
{
  // parse and convert line here
  return true; // if valid return true else return false
}
bool readLine()
{
  if (softSerial1.available())
  {
    char c = softSerial1.read(); //gets one byte from serial buffer
    if ((c == '\n') || (c == '\r'))
      return true; // found line

    readString1 += c;

    if (readString1.length() >= readString_RESERVE)
      return true; // a long line
  }
  xCoo = readString1;
  //  Serial.print("xCoo: ");
  //  Serial.print(xCoo);
  //  Serial.println();
  //  x_1 = xCoo.toInt();
  int i = 0;
  cSFP(sfStr, (char*)xCoo.c_str()); // short for createSafeStringFromCharPtr
  if (sfStr.toInt(i) ) {
    // valid int
    x_1 = i;
  } else {
    Serial.print(sfStr); Serial.println(" is not an int");
  }

  return false;
}

Thank you for fixes.

drmpf:
code

The SafeString tutorial is a bit overwhelming. Is there a shorter version?

I want to replace strings in a big program with SafeStrings. So I have to use createSafeString for each string? And what is "F()" for?

I want to replace strings in a big program with SafeStrings.

Do you understand the difference between strings and Strings ?

For using SafeString replace char str[20] with cSF(str,20) and then use str like an Arduino String (mostly)
cSF( ) is a short cut for createSafeString( )
For char str[] = "test" use **cSF(str,10,"test") ** or use

char str[] = "test";
cSFA(sfStr,str);  // wraps str in a SafeString and picks up the valid size of str

The F() stuff just saves RAM memory regardless of which approach you use.

You could also just start with Arduino Strings and then later convert to SafeStrings if you need to / want to.
To use Arduino Strings in a 'big' program see my Taming Arduino Strings tutorial for the guideline.
The basic SafeString functions are mimic Arduino String functions, very closely in V4 of SafeStrings.

The SafeString tutorial is a bit overwhelming. Is there a shorter version?

Try running some of the (many) example sketches that come with the SafeString library. They cover each feature one at time.
Drop me line if you have any problems with SafeString.

drmpf:

const size_t readString_RESERVE = 100;

String readString1;

void setup()
{
 readString1.reserve(readString_RESERVE);
}

Why is Sting used and not "cSF(readString1, const size_t readString_RESERVE = 100);"?

OK, looks like I am confusing you with Arduino Strings V SafeStrings,
here is some code that just uses SafeStrings and the SafeStringReader which does most of the work

/*
  //https://forum.arduino.cc/index.php?topic=728131
*/
#include <SoftwareSerial.h>
#include <SafeStringReader.h>
// download SafeString library from Arduino library manager
// or from the tutorial page
// https://www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html

SoftwareSerial softSerial1(10, 11); // RX, TX

const size_t MAX_LINE_LENGTH = 20;
createSafeStringReader(sfReader, MAX_LINE_LENGTH, "\r\n"); // use either carriage return or Newline to delimit input

cSF(xCoo, MAX_LINE_LENGTH); // create SafeString large enough to hold a whole line

const byte dirPinX = 4;
const byte stepPinX = 5;
volatile float deltaX = 0;

volatile int x_1, x_2;
volatile int hasRunXA = 0, hasRunXB = 0;
volatile int resetXcount = 0, resetYcount = 0, stepXcount = 0, stepYcount = 0;

void setup()
{
  softSerial1.begin(9600); //Recieve data on pin 10
  Serial.begin(19200); // <<<<<<< 115200 is a better choice
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  Serial.println(F("SafeStringReader_rawCoo.ino"));

  SafeString::setOutput(Serial); // output debug msgs
  //sfReader.connect(Serial); // read from Serial for testing
  sfReader.connect(softSerial1); // read from softSerial1
  if (SafeString::errorDetected()) {
    while (1) {
      Serial.println(F("Error setting up SafeString globals."));
      delay(3000);
    }
  }
  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);
}


// parse and convert line here
bool processLine(SafeString &line)
{
  xCoo = line; // save input line.  Why?
  int i = 0;
  if (line.toInt(i) ) {
    // valid int
    x_1 = i;
  } else {
    Serial.print("'"); Serial.print(line); Serial.println("' is not an int");
    return false; // invalid
  }
  return true; // if valid return true else return false
}

void loop()
{
  if (sfReader.read()) // got a line delmited by \r or \n
  {
    if (processLine(sfReader)) {
      // valid line ...  now have updated x_1
      Serial.print(F("Updated x_1:")); Serial.println(x_1);
    } else {
      Serial.print(F("Invalid input '")); Serial.print(sfReader); Serial.println("'");
    }
    // no need to clear sfReader, read() does that for next line
  }
}

Output is

SafeStringReader_rawCoo.ino
Updated x_1:44
'kd' is not an int
Invalid input 'kd'
'5.5' is not an int
Invalid input '5.5'
  1. Why is SafeStringReader.h included and not SafeString.h?

  2. Why is 115200 a better choice?

  3. Where do you explain "F()"? On another question you mention it saves RAM memory. On the forward website it says it's a macro... I don't know how to use it.

  1. Why is SafeStringReader.h included and not SafeString.h?

SafeStringReader.h #includes SafeString.h
But no harm if you add another
#include "SafeString.h"
as well. There are 'guards' in the .h files so that .h files are only included once regardless of how many #include ".." there are in various headers. So if in doubt, just add the #include

  1. Why is 115200 a better choice?

Serial.prints can block your loop() code if the TX buffer fills up. Using a higher baud rate lets you send more data to the Arduino Monitor without blocking your loop(). If you have a lot of output/debug msgs then you can add an extra non-blocking OutputBuffer, See my Arduino Serial I/O for the Real World for a detailed tutorial.

  1. Where do you explain "F()"? On another question you mention it saves RAM memory. On the forward website it says it's a macro... I don't know how to use it.

If you use as char string line "test" in your code the Compiler puts it in SRAM. On Arduino boards with little memory, like the UNO, you can fill up all you memory with "..." text for output/debugging msgs.
Serial.print and Arduino Strings and SafeStrings all accept a 'special' argument of type
F("text") the F( ) is a macro that tell the Compiler to put the chars in Flash memory instead of SRAM and indicates to the print( ) method, that it needs to use some special code to retrieve the chars.
See my small writeup on What fails when you add lots of Strings to your Arduino program.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.