0

The code was based on Fahad Mirza's very helpful example here Send structure through Serial

The Sender's code now became

struct Gyro_data_structure
{
  char command_name[5];
  float gyro_X;
  float gyro_Y;
  float gyro_Z;
};

Gyro_data_structure Gyro_data;
int size_gyro = sizeof(struct Gyro_data_structure);    

void setup() {
  //Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
  //Serial.begin(115200); // opens serial port, sets data rate to 9600 bps
  Serial.begin(250000);    
}   

void loop() {

  Gyro_data.command_name[0] = 'H';
  Gyro_data.command_name[2] = 'l';
  Gyro_data.gyro_X++;
  Gyro_data.gyro_Y=12;    

  Serial.write('p');
  Serial.write('L');
  Serial.write(Gyro_data.command_name, sizeof(Gyro_data.command_name));


  Serial.write('m');
  Serial.write('x');

  send(&Gyro_data);
  delay(100); //---------------------the parameter played in this post
  //Serial.flush();
}

void send (const Gyro_data_structure* table)
{
  Serial.write((const char*)table, size_gyro);  // 2 bytes.
}

The Receiver's code

struct Gyro_data_structure
{
  char command_name[5];
  float gyro_X;
  float gyro_Y;
  float gyro_Z;
};

Gyro_data_structure  Gyro_data_received;
int size_gyro = sizeof(struct Gyro_data_structure);

void setup() {
  Serial.begin(9600);
  //Serial1.begin(9600);
  //Serial1.begin(115200);
  Serial1.begin(250000);
}


void loop() {

  //if(Serial1.available()>0)      
  if(Serial1.available()>=size_gyro)//----------------worked very welll//------------------------
  {
      Serial.println(Serial1.available());

    //Serial1.findUntil('p','L');
    //Serial1.find('p');
    //Serial1.find('L');

  if ((Serial1.read() == 'p') && (Serial1.read() == 'L'))
  {
    Serial1.readBytes(Gyro_data_received.command_name, sizeof(Gyro_data_received.command_name));

    Serial.println("Received confirmation");
    Serial.println(Gyro_data_received.command_name[0]);
    Serial.println(Gyro_data_received.command_name[2]);


    if ((Serial1.read() == 'm') && (Serial1.read() == 'x')) {

      Serial.println("Gyro data received");
      receive2(&Gyro_data_received);

      Serial.println(Gyro_data_received.command_name[0]);
      Serial.println(Gyro_data_received.gyro_X);
      Serial.println(Gyro_data_received.gyro_Y);
    } else {
      Serial.println("Gyro data lost");
    }

  } else {

    Serial.println("No code confirmed");
    while(Serial1.peek()!='p'){
    Serial.print(Serial1.read()); //basically clean the receiver 
    //Serial1.flush();    
    }


  }

  }
}


bool receive2(Gyro_data_structure* table)
{
  return (Serial1.readBytes((char*)table, sizeof(Gyro_data_structure)) == sizeof(Gyro_data_structure));
}

What it does is for sender send a 5 byte char array and a structure to receiver. If receiver received the "code", i.e. char 'p' 'L' and 'm' 'x' it read the incoming 5byte char array and structure. If the code was not confirmed, it clear the incoming bytes.

However, I encountered the following issue.

First, at receiver's code, one pay attention to

  if(Serial1.available()>0)      
  if(Serial1.available()>=size_gyro)

the condition for which the receiver start to process the read() sequence.

However, at speed(9600), I noticed that if one use

if(Serial1.available()>0)

very often it start to read when Serial1.available()=1, and return incorrect numbers which got cleared out by

Serial.println("No code confirmed");
while(Serial1.peek()!='p'){
Serial.print(Serial1.read()); //basically clean the receiver 
//Serial1.flush();    
}

But if one use

  if(Serial1.available()>=size_gyro)

as the condition to trigger the read(), it worked out very well. On the other hand, if one increase the serial speed to, say 115200, this does not matter, as they both return the correct result. Could you explain to me why this was the issue, please?

Second, please pay attention to

  send(&Gyro_data);
  delay(100);

At speed at or above 100, with nano board and mega board, this worked out fine. But if one wants to increase the transmission speed, change delay(100) to delay(20) in sender's code, the receiver's code returns error. This issue could not be resulted by change the wiring or increase the serial speed. Especially, the receiver's at part

  Serial.println(Serial1.available()); 

usually return a number of 62~63 when there's an error.

Could you explain to me where did those two questions come from? Were they because the chip set on Arduino? Especially, how to achieve a communication response under 20 ms?

  • what two questions are you talking about? – jsotola Jan 30 at 2:20
  • In my answer to your previous question, I wrote: “Binary is, however, harder to handle: [...] you may need to define a protocol of sorts in order to get the framing right. [...] I generally recommend using ASCII unless you really need the extra efficiency of a binary protocol.”. You see what I mean now? – Edgar Bonet Jan 30 at 9:14
  • Do you have an idea what serial speed means? Think about what your program does, and how fast bits and bytes are transfered. – the busybee Jan 30 at 10:53
1

First, you got some logic wrong in the receiver: you have a test for Serial1.available()>=size_gyro and, when this is true, you read size_gyro + 9 bytes. You should change the test in order to only start reading when you actually have size_gyro+9 bytes available.

Then, note that command_name is transmitted twice: first as part of the 9-byte preamble (between "pL" and "mx"), then as part of the transmitted Gyro_data_structure. There is no point in doing so.

at speed(9600), I noticed that if one use if(Serial1.available()>0) very often it start to read when Serial1.available()=1, and return incorrect numbers

This is expected: you wait for 1 byte to be available in the input buffer, then you attempt to read the 9-byte preamble. One of your calls to Serial1.read() is going to find the buffer empty, and it will then return −1.

But if one use if(Serial1.available()>=size_gyro) as the condition to trigger the read(), it worked out very well.

In other words, when you wait until 17 bytes are available before reading the 9-byte preamble, you get at least the preamble right. Is this surprising?

if one increase the serial speed to, say 115200, this does not matter, as they both return the correct result

Your code takes some time to do the reading. At a fast enough baud rate, the serial port receives the incoming bytes faster than your code reads them, so you never run into an empty buffer.

if one wants to increase the transmission speed, change delay(100) to delay(20) in sender's code, the receiver's code returns error

The sender is going too fast, and the receiver cannot cope with the data rate.

Serial1.available() usually returns a number of 62~63 when there's an error.

This means the receive buffer is full.

Note that the receiver would be way faster if it wasn't printing so much. Upon receiving a correct transmission, it will Serial.println() a bunch of messages that should total at least 64 bytes. Transmitting those bytes at 9600 b/s takes more than 66.5 ms.


Edit: proposed solution.

Your idea of prepending a “code” to the data structure in order to identify a correct transmission is in the right track. This is a kind of format indicator commonly known as a “magic number” or a “signature”. It does not ensure correct transmission with 100% confidence, as the signature could be present by accident as part of the data structure itself. But if the signature bytes are unlikely enough to happen by accident, you have a pretty high confidence that wherever you find the signature in the data stream it's the start of a new packet. You can increase this confidence by making the signature longer, at the cost of longer transmission times.

Now, for a matter of simplicity, I would not send the signature as two disjoint parts. Common practice is to send it as one contiguous set of bytes, at the very beginning of the packet. For example, I would send your packets like so:

// 5-byte signature, including the terminating '\0'.
const uint8_t Gyro_signature[] = "Gyro";

void Gyro_send(const Gyro_data_structure &gyro) {
    Serial1.write(Gyro_signature, sizeof Gyro_signature);
    Serial1.write((uint8_t *) &gyro, sizeof gyro);
}

You may want to change the signature to something less likely to happen by accident.

On the receiver side, when you expect the signature, you read from the port one byte at a time while counting how many correct bytes you have so far. The algorithm goes as follows:

  1. let i = 0 be the number of correct bytes received so far
  2. read an incoming byte
  3. if that incoming byte is correct, increment i
  4. otherwise, if we just got the first signature byte, reset i = 1
  5. otherwise reset i = 0
  6. unless we got the full signature, go to 2

This assumes that the first signature byte doesn't appear elsewhere within the signature. Otherwise steps 4–5 would be more complex.

Here is a receiver function that implements this algorithm, then reads the data structure:

void Gyro_receive(Gyro_data_structure &gyro) {

    // Read until we get a complete signature.
    int discarded = 0;
    for (size_t i = 0; i < sizeof Gyro_signature; ) {
        if (Serial1.available()) {
            char c = Serial1.read();
            if (c == Gyro_signature[i]) {  // correct byte
                i++;
            } else if (c == Gyro_signature[0]) {  // first byte
                discarded += i;
                i = 1;
            } else {
                discarded += i+1;
                i = 0;
            }
        }
    }
    if (discarded) {
        Serial.print("Discarded ");
        Serial.print(discarded);
        Serial.println(" bytes.");
    }

    // Wait for the data.
    while ((size_t) Serial1.available() < sizeof gyro)
        /* wait */;

    // Read it.
    Serial1.readBytes((char *) &gyro, sizeof gyro);
}

Note that this function is blocking. If you need a non-blocking version, you should be able to easily convert the signature-checking part into a finite state machine where i is the state variable.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.