Need some help with displaying serial data

I am using SoftwareSerial to write to a serial lcd and also communicate with a scale. The message to the scale is a capital W. The return message from the scale looks like "02 000.00 /r where 02 indicates start of message and the carriage return indicates end of message. It's just ascii data being received.

In my code I attempt to trigger on the 0x02 and then put the rest of the data into a string called inputString. When the '/r' is received then I want to print out the inputString string. I think I've made it where it does not store the start character '02' and the end character '/r' but I get some crazy characters printing.

In my setup I use this statement- inputString.reserve(16); to define the size of the inputString. I've tried making it only 6, 7, & 8 and16 bytes but it always acts the same. Using the logic analyzer going into the Arduino pin the data looks perfect everytime. It seems like I'm overflowing a buffer or reading in data somehow that I don't want. What am I doing wrong?

void loop() {
  delay(2000);                                         
  float uS = sonar[currentSensor].ping_median(20);    // Send ping, get ping time in microseconds (uS).
  Serial.print(uS / US_ROUNDTRIP_IN);
  LCDSerial.write(0x8C);         // Move to line 0 position 12
  LCDSerial.print("       ");    // Clear data area
  LCDSerial.write(0x8C);         // Move to line 0 position 12
  LCDSerial.print(uS / US_ROUNDTRIP_IN); 
  LCDSerial.write(0x22);        // Inches (")
  Serial.println("in");

  ScaleSerial.write(0x57);       // Send "W"
  ScaleSerial.listen();
  inChar = ScaleSerial.read();
  if (inChar = 0x02) {
    while (ScaleSerial.available() > 0) {
      inChar = ScaleSerial.read();
      if (inChar == '\r') {
        Serial.println(inputString);
        LCDSerial.write(0xC8);         // Move to line 0 position 12
        LCDSerial.print("       ");    // Clear data area
        LCDSerial.write(0xC8);         // Move to line 0 position 12
        LCDSerial.print(inputString);
        inputString = "";       
      }
      else {
        inputString += inChar;     
      }
    }
  }
}

I've attached a screen shot of the terminal monitor. It should look like this:

0.00"
000.15
0.00"
000.15

but you can see the garbage in the photo that it prints. The 000.15 is just showing that there is .15 lbs of weight on the scale at present.

Thanks.

 if (inChar = 0x02)

Oops

If you really are using "02 000.00 \r" as the input string, try the code below:

void setup() {

 Serial.begin(9600);

}

void loop() {
  char buffer[20];      // Input test string: "02 000.00 \r"
  int charsRead;

  if (Serial.available() ) {
    charsRead = Serial.readBytesUntil('\r', buffer, sizeof(buffer) - 1);
    buffer[charsRead - 2] = '\0';   // Overwrite carriage return characters '\' and 'r'
    if (buffer[0] == '0' && buffer[1] == '2') {   // Necessary leadin?
      Serial.println(&buffer[2]);
    }
  }

}

If you are testing for the newline character, you'd have to modify the code a little.

Have a look at the 3rd example in Serial Input Basics. Just change the start and end markers to meet your requirement. There is also a parse example.

...R

Robin2:
Have a look at the 3rd example in Serial Input Basics. Just change the start and end markers to meet your requirement. There is also a parse example.

...R

Ok so I looked at your example and incorporated it into my code.

void loop() {
    recvWithStartEndMarkers();
    showNewData();
    delay(2000); 
}

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = 0x02;
    char endMarker = '\r';
    char rc;
    
    ScaleSerial.write(0x57);       // Send "W"
    ScaleSerial.listen();
    
    while (ScaleSerial.available() > 0 && newData == false) {
        rc = ScaleSerial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        Serial.println(receivedChars);
        LCDSerial.write(0xC8);         // Move to line 0 position 12
        LCDSerial.print("       ");    // Clear data area
        LCDSerial.write(0xC8);         // Move to line 0 position 12
        LCDSerial.print(receivedChars);
       // inputString = "";
        newData = false;
    }
}

I still get erroneous data occasionally. See the attached screenshots.

In LogicScale1 you see the 'W' sent and the reply from the scale. However I did happen to catch it messing up 1 time with Logic Analyzer. See LogicScale2. Notice that there is a slight delay after the start byte '0x02'. How is this type of situation handled?

As I mentioned earlier I'm using SoftwareSerial. I use it to communicate with the LCD display. I do not receive anything back from the display. It seems if I comment out the portion of code that communicates with the display then the code will seem to run nearly perfect except for when there is a slight delay after the start byte. As soon as I uncomment the LCDSerial lines then there is more occasional chaos. Is there a conflict with the receive buffer between the LCDSerial and ScaleSerial? Is there a way to ignore the Rx buffer of LCDSerial even though I don't even have it connected?

Were getting closer...

Thanks.

Arduino2.jpg

LogicScale2.jpg

Here's something else that may be causing an issue. I notice that I don't always get the data displayed on the terminal. The terminal doesn't always display after the scale has been sent a command and data received.

Notice that there had been 8 commands and responses from the scale but only 5 of them displayed. So maybe there's residual data in the buffer?

Well I found something while searching other serial examples and it seems to have helped quite a bit but there's still an issue with it not always displaying every time it communicates with the scale. I added this to the beginning of the code:

 #define ScalerxPin 2
 #define ScaletxPin 3
 #define LCDrxPin 7
 #define LCDtxPin 8

And this to the setup method:

  pinMode(ScalerxPin, INPUT);
  pinMode(ScaletxPin, OUTPUT);
  pinMode(ScalerxPin, INPUT);
  pinMode(ScaletxPin, OUTPUT);

As I mentioned before if I don't communicate at all with the LCD display (by commenting out the LCDSerial write / print routines) and just watch the data on the serial terminal it works fine.

Any ideas or suggestions anyone?

I've considered going to the Mega2560 just to add the additional hardware serial ports or even going back to the Parallax Propeller.

Any ideas or suggestions anyone?

Post your code, post your observations.

Slow down a bit - I can't keep up with all of that. Post your latest code (a complete program) with a description of how it behaves.

...R

Here's my code...

// ---------------------------------------------------------------------------
// Automatically measure and weigh a box.
// ---------------------------------------------------------------------------

#include <NewPing.h>
#include <SoftwareSerial.h>

#define ScalerxPin 2
#define ScaletxPin 3
#define LCDrxPin 7
#define LCDtxPin 8

SoftwareSerial LCDSerial(LCDrxPin, LCDtxPin);         // (RX, TX) Serial comms to LCD display on pin 8 only. 
SoftwareSerial ScaleSerial(ScalerxPin, ScaletxPin);   // (RX, TX) Serial comms to Scale. 

#define T_TRIGGER_PIN  9   // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define T_ECHO_PIN     9   // Arduino pin tied to echo pin on the ultrasonic sensor.
#define L_TRIGGER_PIN  10  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define L_ECHO_PIN     10  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define R_TRIGGER_PIN  11  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define R_ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define F_TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define F_ECHO_PIN     12  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define B_TRIGGER_PIN  13  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define B_ECHO_PIN     13  // Arduino pin tied to echo pin on the ultrasonic sensor.

#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
#define SONAR_NUM     5 // Number of sensors.
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

float box_width;
int   box_width_correction = 18;
unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.
String inputString = "";            // a string to hold incoming data
char inChar;
const byte numChars = 7;
char receivedChars[numChars];

boolean newData = false;

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(T_TRIGGER_PIN, T_ECHO_PIN, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(L_TRIGGER_PIN, L_ECHO_PIN, MAX_DISTANCE),
  NewPing(R_TRIGGER_PIN, R_ECHO_PIN, MAX_DISTANCE),
  NewPing(F_TRIGGER_PIN, F_ECHO_PIN, MAX_DISTANCE),
  NewPing(B_TRIGGER_PIN, B_ECHO_PIN, MAX_DISTANCE)
};


void setup() {
  Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results.
  
  pinMode(ScalerxPin, INPUT);
  pinMode(ScaletxPin, OUTPUT);
  pinMode(ScalerxPin, INPUT);
  pinMode(ScaletxPin, OUTPUT);
  
  ScaleSerial.begin(9600); // Setup software serial for Scale
  // reserve 16 bytes for the inputString:
  inputString.reserve(16); 
  LCDSerial.begin(9600);  // Setup software serial for LCD display
  LCDSerial.write(0x0C); // Clear display
  LCDSerial.write(0x11); // Turn backlight on
  LCDSerial.write(0x16); // Turn cursor off
  LCDSerial.write(0x80); // Move cursor home
  LCDSerial.println("     Box Wizard     ");
  delay(1500);
  LCDSerial.print("Measure ");
  delay(1000);
  LCDSerial.print("Weigh ");
  delay(1000);
  LCDSerial.print("Ship!");
  delay(2000);
  LCDSerial.write(0x0C); // Clear display
  LCDSerial.write(0x80); // Move cursor home
  LCDSerial.print("Box Width :");
  LCDSerial.write(0x94); // Move cursor to line 1 position 0
  LCDSerial.print("Box Height:");
  LCDSerial.write(0xA8); // Move cursor to line 2 position 0
  LCDSerial.print("Box Depth :");
  LCDSerial.write(0xBC); // Move cursor to line 3 position 0
  LCDSerial.print("Box Weight:");
  
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}
/*
void loop() {
  delay(2000);                                         
  float uS = sonar[currentSensor].ping_median(20);    // Send ping, get ping time in microseconds (uS).
  Serial.print(uS / US_ROUNDTRIP_IN);
  LCDSerial.write(0x8C);         // Move to line 0 position 12
  LCDSerial.print("       ");    // Clear data area
  LCDSerial.write(0x8C);         // Move to line 0 position 12
  LCDSerial.print(uS / US_ROUNDTRIP_IN); 
  LCDSerial.write(0x22);        // Inches (")
  Serial.println("in");

  ScaleSerial.write(0x57);       // Send "W"
  ScaleSerial.listen();
  inChar = ScaleSerial.read();
  if (inChar == 2) {
    while (ScaleSerial.available() > 0) {
      inChar = ScaleSerial.read();
      if (inChar == '\r') {
        Serial.println(inputString);
        LCDSerial.write(0xC8);         // Move to line 0 position 12
        LCDSerial.print("       ");    // Clear data area
        LCDSerial.write(0xC8);         // Move to line 0 position 12
        LCDSerial.print(inputString);
        inputString = "";       
      }
      else {
        inputString += inChar;     
      }
    }
  }
}    
*/
void loop() {
    recvWithStartEndMarkers();
    showNewData();
    delay(2000); 
}

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = 0x02;
    char endMarker = 0x0D;
    char rc;

    LCDSerial.flush();
    ScaleSerial.flush();
    ScaleSerial.write(0x57);       // Send "W"
    ScaleSerial.listen();
    
    while (ScaleSerial.available() > 0 && newData == false) {
        rc = ScaleSerial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = 0x00; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        Serial.println(receivedChars);
        //LCDSerial.listen();
        //LCDSerial.flush();
        //LCDSerial.write(0xC8);         // Move to line 0 position 12
        //LCDSerial.print("       ");    // Clear data area
        LCDSerial.write(0xC8);         // Move to line 0 position 12
        LCDSerial.print(receivedChars);
        newData = false;
    }
}

The problem is with receiving data consistently from ScaleSerial. It doesn't always read the data even though the logic analyzer shows it being sent to the Arduino. If I comment out the LCDSerial commands it seems to work fine just displaying on the terminal. Seems as though there is a problem using multiple instances of Software Serial as I have read elsewhere. Unfortunately for the Uno that's the only choice.

If you see something that may be able to save this please say so. Otherwise I'm going to jump ship and develop this project on another platform.

Thanks in advance.

It doesn't always read the data even though the logic analyzer shows it being sent to the Arduino.

Only one instance of SoftwareSerial can be listening (or talking) at a time. While you are talking to the LCD, you are NOT listening to the scale.

    ScaleSerial.write(0x57);       // Send "W"
    ScaleSerial.listen();

Any call to a SoftwareSerial method makes the instance that the method is called on the listening instance. There is no need to explicitly listed to the instance you just wrote to.

Otherwise I'm going to jump ship and develop this project on another platform.

A Mega?

Or change the LCD for an I2C one.

Yet another example of the value of posting a complete program. Without it we would not have known that you are trying to use 2 instances of SoftwareSerial.

Another (much better) option is to use a Mega which has 3 spare HardwareSerial ports.

...R

Robin2:
Yet another example of the value of posting a complete program. Without it we would not have known that you are trying to use 2 instances of SoftwareSerial.

It was mentioned in the opening post :wink:

PaulS:
Only one instance of SoftwareSerial can be listening (or talking) at a time. While you are talking to the LCD, you are NOT listening to the scale.

PaulS you got me thinking here... I know that the limitation of Software Serial is not being able to listen on multiple instances at the same time as I had read that while studying this. So what I did was connect the Logic Analyzer to the LCD along with the Scale comms. I was surprised to see that it was talking to the LCD right after it sends the 'W' and sometimes it overlaps into the receive from the Scale. See attached photos.

Here you can see that it sometimes does not write to the LCD...

I was thinking that the code was more linear in that it would 1. Send 'W' to scale then 2. Receive data from Scale then 3. Talk to LCD and display data then repeat the process but I see that I'm wrong so I need to rewrite the code so that it works in that order. And since you mentioned that once I initiate a write to the serial port that then puts that port in listen mode I'll use that info.

In regards to the "jump ship"... No not a Mega but a Parallax Propeller. In fact in my frustration last night I already have it working with the Prop. I was leaning towards using the Arduino since there are some neat add on boards that I am using in this project whereas the Prop I have to hand build my prototype. But at least it works.

So maybe with this new found realization I can still manage to make the Arduino work.

  ScaleSerial.write(0x57);       // Send "W"
  ScaleSerial.listen();
  inChar = ScaleSerial.read();

How long are you waiting for a response? At 9600 baud, the character hasn't even been shifted out to the device before you expect a response back. Not only that, you expect the COMPLETE response.

Yes, you need to rewrite your code, with your new understanding of the asynchronous nature of serial communication.

It can take upwards of 50mS before the Scale will respond. Not always but that is the longest I have seen.

How do you set the Arduino Software Serial to wait for a response?

How do you set the Arduino Software Serial to wait for a response?

You don't.

You can make the Arduino wait for a response, IF you know what marks the end of the response.

char eop = '?'; // Set this to whatever represents the end of a packet
   // Send a read command
   ScaleSerial.write('W');

   // Diddle around until the scale responds
   char resp = '!';
   while(resp != eop)
   {
      while(ScaleSerial.available() == 0); // Do nothing until there is data to read

      // Read the next character in the packet
      resp = ScaleSerial.read();

      // If it's not the end of the packet, print the character
      if(resp != eop)
         Serial.print(resp);
   }

sterretje:
It was mentioned in the opening post :wink:

I need to be told everything twice.

Even with your help I found it hard to find the reference :slight_smile:

...R