Go Down

Topic: Serial communication is mismatched (Read 187 times) previous topic - next topic

Henradrie

Hi all,

I have a master slave network where the master sends a character to the slave and the slave sends data back. The master then reads it and prints the output to the serial monitor. The problem is that the order is sometimes messed up. Everything is there but the order wrong. The order starts correct then changes randomly.

Here is how the data is sent from the slave. I'm transmitting using a MAX485 chip so I have to use delays and a control pin.

Code: [Select]
    if (Serial.find(address)) { //Outputs data only if called for

      digitalWrite(control, HIGH);  //transmit enable
      delay(20);

      //Print gyro data
      Serial.println(GX);
      Serial.println(GY);
      Serial.println(GZ);

      //Print accel data
      Serial.println(AX);
      Serial.println(AY);
      Serial.println(AZ);

      //Print magnetometer data
      Serial.println(MX);
      Serial.println(MY);
      Serial.println(MZ);
      Serial.flush();      //send out all data before closing communication module
      digitalWrite(control, LOW);   //receive enable
      delay(20);
    }


Here is the master receiving the code
Code: [Select]
  digitalWrite(control, HIGH);  //transmit enable
  delay(20);
  Serial2.println('B');     //request to send data
  Serial2.flush();        //make sure data is sent before setting pin to recieve
  digitalWrite(control, LOW);    //recieve enable
  delay(20);
  Serial.print("Bytes in port before read ");
  Serial.println(Serial2.available());
  if (Serial2.available()) {
    for (i = 0; i < 9; i++) {
      inputB[i] = Serial2.parseInt();
    }
    Serial.print("Bytes in port after read ");
    Serial.println(Serial2.available());
    while (Serial2.available()>1) {  //empty the serial buffer
      Serial2.read();
    }
  } else {
    Serial.println("Not Available");
  }

  //Outputs data to computer via USB and also to datalogger

  for (i = 0; i < 3; i++) {
    Serial.print(inputB[i]);
    Serial.print(",");
  }
  for (i = 3; i < 7; i++) {
    Serial.print(inputB[i]);
    Serial.print(",");
  }
  for (i = 7; i < 9; i++) {
    Serial.print(inputB[i]);
    Serial.print(",");
  }
  int stamp = now();
  Serial.print(stamp);
  Serial.println("");
  Serial.println(" ");
  Serial.flush();
  delay(250);


Here is a printout of what the serial monitor displays. The last number is a timestamp added in the master, it never changes position
Code: [Select]

Bytes in port before read 5
Bytes in port after read 1
-12,52,-14,-20,25,1191,-2396,-1011,-4709,0
 
Bytes in port before read 55
Bytes in port after read 7
-12,66,-13,-21,26,1193,-2380,-997,-4743,0
 
Bytes in port before read 63
Bytes in port after read 15
57,-10,-20,25,1192,-2395,-997,-4743,-21,1         (notice the shift here, 2nd last digit should be -4743)
 
Bytes in port before read 50
Bytes in port after read 1
56,-11,-19,24,1192,-2407,-1053,-4694,-16,1



Here is another showing 2 shifts
Code: [Select]

Bytes in port before read 50
Bytes in port after read 1
53,-10,-20,27,1192,-2394,-1039,-4710,-10,138
 
Bytes in port before read 50
Bytes in port after read 1
57,-11,-21,27,1193,-2350,-1081,-4727,-14,139
 
Bytes in port before read 60
Bytes in port after read 3
58,-9,-20,27,1193,-2409,-1039,-4710,-4727,139
 
Bytes in port before read 54
Bytes in port after read 7
-11,48,-21,-19,25,1191,-2423,-1066,-4745,139
 
Bytes in port before read 63
Bytes in port after read 15
57,-11,-24,27,1190,-2380,-1025,-4727,-7,140
 
Bytes in port before read 63
Bytes in port after read 18
56,-7,-21,25,1192,-2380,-1025,-4709,-8,140


Anybody have any idea how or why this is happening and what I can do about it?

Robin2

I can't make sense of your description. Can you post an example of the correct data and the erroneous data on two separate lines so the differences are obvious.

I wonder would the problem be solved by the use of start- and end-markers as in the 3rd example of Serial Input Basics

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

PaulS

Code: [Select]
  Serial.print("Bytes in port before read ");
  Serial.println(Serial2.available());
  if (Serial2.available()) {
    for (i = 0; i < 9; i++) {
      inputB[i] = Serial2.parseInt();
    }

If there is one or more bytes available to read, it hardly seems reasonable to assume that 9 ints with delimiters are present to be read.

Considering that the 9 ints with separators could use 54 bytes, and you have a 64 byte buffer, I'd suspect that data loss could be happening.

Code: [Select]
    Serial.print("Bytes in port after read ");
    Serial.println(Serial2.available());
    while (Serial2.available()>1) {  //empty the serial buffer
      Serial2.read();
    }

Throwing away random amounts of unread data is not the mark of the sharpest crayon in the box.

Henradrie

Here is the results labeled a bit better.

Code: [Select]


Bytes in port before read 5
Bytes in port after read 1
-12,52,-14,-20,25,1191,-2396,-1011,-4709,0
 
Bytes in port before read 55
Bytes in port after read 7
-12,66,-13,-21,26,1193,-2380,-997,-4743,0
 
Bytes in port before read 63
Bytes in port after read 15
57,-10,-20,25,1192,-2395,-997,-4743,-21,1         (notice the shift here, 2nd last digit should be -4743)
 
Bytes in port before read 50
Bytes in port after read 1
56,-11,-19,24,1192,-2407,-1053,-4694,-16,1



Here is another showing 2 shifts
Code: [Select]

Bytes in port before read 50
Bytes in port after read 1
53,-10,-20,27,1192,-2394,-1039,-4710,-10,138    Incorrect
 
Bytes in port before read 50
Bytes in port after read 1
57,-11,-21,27,1193,-2350,-1081,-4727,-14,139    Incorrect
 
Bytes in port before read 60
Bytes in port after read 3
58,-9,-20,27,1193,-2409,-1039,-4710,-4727,139  Correct
 
Bytes in port before read 54
Bytes in port after read 7
-11,48,-21,-19,25,1191,-2423,-1066,-4745,139    Correct
 
Bytes in port before read 63
Bytes in port after read 15
57,-11,-24,27,1190,-2380,-1025,-4727,-7,140      Incorrect
 
Bytes in port before read 63
Bytes in port after read 18
56,-7,-21,25,1192,-2380,-1025,-4709,-8,140        Incorrect


I don't understand why there would be data loss. All integers are filled up with values. If there was data loss wouldn't there be zeros?

I do understand that throwing away data isn't the best idea but since nothing else was working I figured it would be worth a shot. That bit of code probably won't make it into the final cut.

I've looked at the start and end bit stuff and try to add it. Maybe that will solve my issue.

Robin2

You have posted lines marked "incorrect" but I can't see what they should be - what would be the correct version of that line.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Henradrie

My mistake, Here is what it is and what it should be.

The second last term should be the first term.

Code: [Select]

Bytes in port before read 50
Bytes in port after read 1
57,-11,-21,27,1193,-2350,-1081,-4727,-14,139    Incorrect
(should be -14,57,-11,-21,27,1193,-2350,-1081,-4727,139)
 
Bytes in port before read 60
Bytes in port after read 3
58,-9,-20,27,1193,-2409,-1039,-4710,-4727,139  Correct
 
Bytes in port before read 54
Bytes in port after read 7
-11,48,-21,-19,25,1191,-2423,-1066,-4745,139    Correct
 
Bytes in port before read 63
Bytes in port after read 15
57,-11,-24,27,1190,-2380,-1025,-4727,-7,140      Incorrect
(should be -7,57,-11,-24,27,1190,-2380,-1025,-4727,140)

PaulS

Quote
I don't understand why there would be data loss. All integers are filled up with values. If there was data loss wouldn't there be zeros?
Suppose that you have a 5 gallon (or liter) bucket (represents the serial buffer).

Suppose that you are dipping out a cup per hour (using Serial.read() or things that use it). Suppose that I am using a fire hose to fill the bucket.

What is going to happen to the water in the bucket (the data in the buffer) when the bucket gets full?  Nothing, nada, zip,zilch.

What is going to happen to the water I'm dumping into the already full bucket? It's going to overflow, and run down the drain. Will you even know?

Robin2

This
Quote
57,-11,-21,27,1193,-2350,-1081,-4727,-14,139    Incorrect
(should be -14,57,-11,-21,27,1193,-2350,-1081,-4727,139)
is a very strange error.

How is the data formatted by the device doing the sending?

Maybe I can set up a demo between two Arduinos.

What ever shortcomings there may be (none, I hope) in Serial Input Basics I don't think it could give that sort of error.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

PaulS

Quote
This ... is a very strange error.
Not really. If the sending device is sending data faster than the Arduino is reading it, there will be data loss issues. That looks, to me, like what is happening in OPs case.

It is unfortunate that OP won't post complete code.

Robin2

Not really. If the sending device is sending data faster than the Arduino is reading it,
Maybe, but in that case I would expect more significant errors.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Henradrie

I've got no problem posting the complete code but I didn't want to deter anybody due to the size of the sketches so I just included the relevant parts.

I've also simplified the slave to make the real issue more clear. Here is a copy of the output. Everything is in proper order but the whole sketch runs a bit too slow. I believe it has something to do with serial timeouts. I also changed the master to look for a start token given by the slave before it sends the data set. It then parses out the next 9 integers before sending the request token and starting the process over again.

I've solved the order problem but now have a speed problem. Next I'll try to stream data into an array of bytes with a start and end bit and decode it from there. Hopefully that will solve some speed problems.

Here is the complete code for the slave
Code: [Select]


#include <ADIS16448.h>
#include <SPI.h>

// Initialize Variables
// Accelerometer
int AX = 0;
int AY = 0;
int AZ = 0;
float AXS = 0;
float AYS = 0;
float AZS = 0;
// Gyro
int GX = 0;
int GY = 0;
int GZ = 0;
float GXS = 0;
float GYS = 0;
float GZS = 0;
// Magnetometer
int MX = 0;
int MY = 0;
int MZ = 0;
float MXS = 0;
float MYS = 0;
float MZS = 0;
// Barometer
int BAR = 0;
float BARS = 0;
// Control Registers
int MSC = 0;
int SENS = 0;
int SMPL = 0;
// Temperature

// Data Ready Flag
boolean validData = false;

// Call ADIS16448 Class
ADIS16448 IMU(7, 2, 4); //ChipSelect,DataReady,Reset Pin Assignments

int control = 3; //set control pin for RS485 communication
//turn high to transmit, low to recieve
char address[] = "B";

void setup()
{
  Serial.begin(19200); // Initialize serial output via USB
  delay(20);
  Serial.setTimeout(250);
  IMU.configSPI(); // Configure SPI communication
  delay(20);
  pinMode(control, OUTPUT);
  digitalWrite(control, LOW); // set RS485 to recieve


  delay(10000); // Give the part time to start up
  //gyroscope callibration for 80 seconds, do not move during this time
  /*IMU.regWrite(SENS_AVG,0x101);
  delay(20);
  IMU.regWrite(SMPL_PRD,0xF01); // Set Decimation on IMU
  delay(85000);  //Waits for gyroscopes to auto calibrate
  IMU.regWrite(GLOB_CMD,0x0001);
  delay(100); //give time for gyroscope values to be written to memory

  */IMU.regWrite(MSC_CTRL, 0x06); // Enable Data Ready on IMU
  delay(20);
  IMU.regWrite(SENS_AVG, 0x104); // Set Digital Filter on IMU to lowest level for maximum resolution
  delay(20);
  IMU.regWrite(SMPL_PRD, 0x301); // Set Decimation on IMU ~136 SPS
  delay(20);

  attachInterrupt(0, setDRFlag, RISING); // Attach interrupt to pin 2. Trigger on the rising edge

}

// Function used to read register values via SPI and load them into variables in LSBs
void grabData()
{
  // Put all the Data Registers you want to read here
  //IMU.configSPI(); // Configure SPI before the read
  GX = IMU.regRead(XGYRO_OUT);
  GY = IMU.regRead(YGYRO_OUT);
  GZ = IMU.regRead(ZGYRO_OUT);
  AX = IMU.regRead(XACCL_OUT);
  AY = IMU.regRead(YACCL_OUT);
  AZ = IMU.regRead(ZACCL_OUT);
  MX = IMU.regRead(XMAGN_OUT);
  MY = IMU.regRead(YMAGN_OUT);
  MZ = IMU.regRead(ZMAGN_OUT);
}

// Function used to scale all acquired data (scaling functions are included in ADIS16448.cpp)
void scaleData()
{
  GXS = IMU.gyroScale(GX) / 4; //Scale X Gyro
  GYS = IMU.gyroScale(GY) / 4; //Scale Y Gyro
  GZS = IMU.gyroScale(GZ) / 4; //Scale Z Gyro
  AXS = IMU.accelScale(AX); //Scale X Accel
  AYS = IMU.accelScale(AY); //Scale Y Accel
  AZS = IMU.accelScale(AZ); //Scale Z Accel
  MXS = IMU.magnetometerScale(MX); //Scale X Magnetometer
  MYS = IMU.magnetometerScale(MY); //Scale Y Magnetometer
  MZS = IMU.magnetometerScale(MZ); //Scale Z Magnetometer

}

// Data Ready Interrupt Routine
void setDRFlag()
{
  validData = !validData;
}

// Main loop. Scale and display registers read using the interrupt
void loop()
{

  if (validData) // If data present in the ADIS16448 registers is valid...
  {
    grabData(); // Grab data from the IMU

    //scaleData(); // Scale data acquired from the IMU
    if (Serial.find(address)) { //Outputs data only if called for

      digitalWrite(control, HIGH);  //transmit enable
      delay(20);
      
      //start character
      Serial.print('S');

      //Print gyro data
      Serial.print(1111);
      //Serial.print(GX);
      Serial.print(',');
      Serial.print(2222);
      //Serial.print(GY);
      Serial.print(',');
      Serial.print(3333);
      //Serial.print(GZ);
      Serial.print(',');

      //Print accel data
      Serial.print(4444);
      //Serial.print(AX);
      Serial.print(',');
      Serial.print(5555);
      //Serial.print(AY);
      Serial.print(',');
      Serial.print(6666);
      //Serial.print(AZ);
      Serial.print(',');

      //Print magnetometer data
      Serial.print(7777);
      //Serial.print(MX);
      Serial.print(',');
      Serial.print(8888);
     // Serial.print(MY);
      Serial.print(',');
      Serial.print(9999);
      //Serial.println(MZ);    //newline character is data finished token
      Serial.flush();      //send out all data before closing communication module
      digitalWrite(control, LOW);   //recieve enable
      delay(20);
    }


    /* if (Serial.find(address)) { //Outputs data only if called for

       digitalWrite(control, HIGH);
       delay(20);

       //Print scaled gyro data
       Serial.println(GXS);
       Serial.println(GYS);
       Serial.println(GZS);

       //Print scaled accel data
       Serial.println(AXS, 6);
       Serial.println(AYS, 6);
       Serial.println(AZS, 6);

       //Print scaled magnetometer data
       Serial.println(MXS, 3);
       Serial.println(MYS, 3);
       Serial.println(MZS, 3);
       Serial.flush();      //send out all data before closing communication module
       digitalWrite(control, LOW);
       delay(20);
     }
     */
  }
  validData = !validData;
}



 


Henradrie

Here is the complete code for the master
Code: [Select]

#include <TimeLib.h>

//initializing inputs and outputs
int control = 22;
int calibration = 24;
int recording = 26;

int collectSwitch = 30;
int collectSwitchLED = 28;

int streamLED = 46;
int storageLED = 48;
int stream = 52;
int storage = 50;

int inputA[9];
int inputB[9];
int i;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(19200);  //computer
  Serial1.begin(38400); //recorder
  Serial2.begin(19200); //instrument
  Serial2.setTimeout(500);
  delay(20);
  pinMode(control, OUTPUT);
  digitalWrite(control, LOW);

  // try this instead
  /* if (digitalRead(collectSwitch) == 1) {
     Serial.println("stuck");
     while (digitalRead(collectSwitch) == 1) ; // hang until keypressed
   }
   Serial.println("Starting collection run");
   /*
   while (digitalRead(collectSwitch) == 1) {
     //wait until collect data switch is pressed
     Serial.println("stuck");
   }
   */
  digitalWrite(collectSwitchLED, HIGH);
  setTime(0);
}

void loop() {

  // put your main code here, to run repeatedly:
  // if (digitalRead(collectSwitch) == 0) {   //collect data switch is on


  //Obtains data from instrument using RS485 adaptors

  digitalWrite(control, HIGH);  //transmit enable
  delay(20);
  Serial2.println('B');     //request to send data
  Serial2.flush();        //make sure data is sent before setting pin to recieve
  digitalWrite(control, LOW);    //recieve enable
  delay(20);

  if (Serial2.find('S')) {
    for (i = 0; i < 9; i++) {
      inputB[i] = Serial2.parseInt();
    }
  }

  /*Serial.print("Bytes in port before read ");
    if (Serial2.available()) {
    Serial.println(Serial2.available());
    for (i = 0; i < 9; i++) {
      inputB[i] = Serial2.parseInt();
    }
    Serial.print("Bytes in port after read ");
    Serial.println(Serial2.available());
  } else {
    Serial.println("Not Available");
    while(Serial2.available()>1){
      Serial2.read();
    }
  }
  */
  /*while (Serial2.available() > 0) {
    char buffer[12];
    Serial2.readBytesUntil(',', buffer, 4);
    Serial.print(buffer);
    //Serial.print(Serial2.parseInt());
    Serial.print(" ");
  }
  Serial.println(" ");
  */
  //Outputs data to computer via USB and also to datalogger

  for (i = 0; i < 3; i++) {
    Serial.print(inputB[i]);
    Serial.print(",");
  }
  for (i = 3; i < 7; i++) {
    Serial.print(inputB[i]);
    Serial.print(",");
  }
  for (i = 7; i < 9; i++) {
    Serial.print(inputB[i]);
    Serial.print(",");
  }
  int stamp = now();
  Serial.print(stamp);
  Serial.println("");
  Serial.println(" ");
  Serial.flush();
  delay(250);
  //} else {  //when collect data switch is off

  // Serial.println("end");

  //}
}

float accelScale(int sensorData)
{
  int signedData = 0;
  int isNeg = sensorData & 0x8000;
  if (isNeg == 0x8000) // If the number is negative, scale and sign the output
    signedData = sensorData - 0xFFFF;
  else
    signedData = sensorData; // Else return the raw number
  float finalData = signedData * 0.000833; // Multiply by accel sensitivity (250 uG/LSB)
  return finalData;
}

float gyroScale(int sensorData)
{
  int signedData = 0;
  int isNeg = sensorData & 0x8000;
  if (isNeg == 0x8000) // If the number is negative, scale and sign the output
    signedData = sensorData - 0xFFFF;
  else
    signedData = sensorData;
  float finalData = signedData * 0.01; // Multiply by gyro sensitivity (0.005 dps/LSB) Sensativity can change
  return finalData;
}

float magnetScale(int sensorData)
{
  int signedData = 0;
  int isNeg = sensorData & 0x8000;
  if (isNeg == 0x8000) // If the number is negative, scale and sign the output
    signedData = sensorData - 0xFFFF;
  else
    signedData = sensorData;
  float finalData = (signedData * 0.1429); // Multiply by sensor resolution (142.9 uGa/LSB)
  return finalData;
}



Despite my best efforts to be clear and concise it seems that I have not been so in this thread. What could I do to be better at this?

Thanks for your help. I've been really stumped on this and you guys have given me ideas on how to move forward.

Robin2

You need a more robust way to receive data. Have a look at the examples in Serial Input Basics (which I linked to in Reply #1).

You can create a function like recvWithStartEndMarkers() for each serial port.

Code like while (Serial.available() > 0 is not robust because the Arduino is much faster than the incoming data.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy