Nano Every ttyACM0 serial over USB issue

I am having a problem with the arduino nano every dropping a byte or 2 after sending several packets, it doesn't seem to matter what baud rate I'm set to 9600-115200 it still frequently drops bytes.

Currently the data is being sent as a union of a struct and a buffer.

typedef struct ARC_Struct{
    uint8_t zero;
    uint8_t magic;
    uint8_t zero2;
    uint8_t magic2;
    // uint32_t count;
    uint32_t curr_dt;
    int32_t d_t;
    int16_t servo_enc;
    int16_t motor_enc;
};

union ARC_Union{
    struct ARC_Struct data;
    uint8_t buffer[sizeof(struct ARC_Struct)];
};

I am sending using this code:

    if(Serial.availableForWrite()){
        Serial.write(packet.buffer, sizeof(union ARC_Union));
    }

The packet is 16 bytes long, the first 4 bytes are to identify the start of the packed and make sure all of the received bytes are aligned using 0x00 0xFD 0x00 0xFD.

The receiving end checks that those four bytes are aligned in the buffer then adds the final 12 bytes in the proper sequence.

I get a random count of clean packets between 30-60 then I normally get a corrupted one caused by a dropped byte or two.

I dumped about 10 seconds of binary values read from the uart. I haven't determined a specific pattern to which bytes are dropped just that it happens abnormally frequently.

I have tried to delay the loop to prevent over filling of the buffer based on the baud rate and packet size but it doesn't help. Anybody have any idea why the nano every has this UART issue, I read that for some of the other Arduino boards that there is a timing issue with imprecise crystals? Makes me wonder what the point is including a UART if it can't meet the timing standards for reliability?

Since I'm using the USB UART TTYACM0 there's no way I can verify the period on an oscilloscope. Anybody know what's going on?

On Arduino boards with native USB like this, the baud rate you set actually has no effect at all. It's only left in to keep the code compatible with other Arduino models that need/use a separate USB to serial converter chip. The data is always sent at USB 1.1 speed (12MB/s).

I'm not sure that's necessary. Can you not simply use

Serial.write(dataStruct, sizeof(dataStruct));

That basically reads "If there is space to send one byte, send a complete struct". You should check if there is enough space to send the struct (e.g. compare with sizeof(ArcStruct)).

I don't have a Nano Every (and a little lazy to dig through the core files) so can't advise further. On AVR based boards (e.g. classic Nano), the code would simply block if the buffer is full.

What is the receiving application? Did you test with another terminal application that allows to display received data in hex?

Nano Every is not a “native usb” board.

This could be a bug in the samd11 used as the UART to usb converter…

1 Like

Oops, my bad.

I've usually found that boards with native USB appear as "ttyACM0" or similar and boards with dedicated USB to serial chips appear as "ttyUSB0" or whatever. That may just be the particular boards and Linux distro I have. Don't have an Every, or any other boards that use a second AVR or SAMD chip as their USB to serial adapter.

An original Uno or Mega (with 16U2 or 8U2) identify itself as /dev/ttyACM*. The boards that become /dev/ttyUSB* are those with generic serial-to-usb converters (CH340, FT232 etc).

The only exception seems to be one of the Nano33 boards; I don't have one so have not had a need in looking at the why.

1 Like

I'm just using the same union on the send and receive end after making sure that the byte packing was the same, since I'm concerned sent byte alignment I read it in 1 byte at a time on the receive end.

I should go back to trying to send 1 byte at a time in a while loop then?

I'm receiving it in my own c code. My current data stream check simply creates a buffer large enough for 10 seconds based on the data rate then dumps it into a binary file. I've been reading it in the VS code hex reader plugin. I can clearly see I'm consistently getting 30+ clean packets, then get random byte drops. causing the values to become corrupt. I'll try to post my receive code soon, but I don't think its the problem.

The linux machine is a Jetson Xavier.

No. Read again what I wrote :wink: If you use availableForWrite(), make proper use of it; I don't think that there is a need (but I'm not 100% sure).

I have no idea what the hex reader is that you're talking about; does it read the serial input and displays it in hex. I would be tempted to use minicom to save the data to file and use od to analyse it to confirm the results of that reader and your application.

I suggest that you post your complete code (both Arduino and your linux application) or short examples that demonstrate the issue.

Is there a fix for this bug?
I haven't noticed byte drops happening sending strings of chars, but I haven't tried capturing a long sequence of strings. I wonder if there is some fundamental difference in how Serial.write and Serial.print work?

I am opening up the ttyACM0 as a file descriptor and reading into a buffer then dumping the buffer to a file.

int i = 0;
int n = 0;

uint8_t big_buff[sizeof(union ARC_Union) * some_multiplier];

while(i < sizeof(big_buff){
    n = read(fd, &big_buff[i], 1);
    if(n > 0) {
        i += n;
    }
}

I then create a new file and write the buffer directly to the file and read it in the VS Code hexeditor. It's very handy because I can view as many byte columns as I need to show that bytes are aligned properly. It is very easy to see when the 00 FD 00 FD pattern gets shifted due to dropped bytes.

I realized that wasn't an accurate version of my serial stream capture code so I updated it to represent how I'm grabbing and storing the received UART bytes.

When I get back to my laptop I'll post the code which is pretty simple.

Here is some of the duino code:

void setup() {
  Serial.begin(9600);

  pinMode(PHASE_A_servo, INPUT_PULLUP);
  pinMode(PHASE_B_servo, INPUT_PULLUP);
  pinMode(PHASE_C_servo, INPUT_PULLUP);

  attachInterrupt(PHASE_A_servo, calc_A_servo, CHANGE);
  attachInterrupt(PHASE_B_servo, calc_B_servo, CHANGE);
  attachInterrupt(PHASE_C_servo, calc_C_servo, CHANGE);

  pinMode(PHASE_A_motor, INPUT_PULLUP);
  pinMode(PHASE_B_motor, INPUT_PULLUP);
  pinMode(PHASE_C_motor, INPUT_PULLUP);

  attachInterrupt(PHASE_A_motor, calc_A_motor, CHANGE);
  attachInterrupt(PHASE_B_motor, calc_B_motor, CHANGE);
  attachInterrupt(PHASE_C_motor, calc_C_motor, CHANGE);

  packet.data.zero = 0;
  packet.data.magic_num = MAGIC;
  packet.data.zero2 = 0;
  packet.data.magic_num2 = MAGIC;
  //packet.data.count = 0;
  packet.data.curr_dt = 1;
  packet.data.d_t = 2;
  packet.data.servo_enc = 3;
  packet.data.motor_enc = 4;
}



void loop() {
  packet.data.curr_dt = micros();
  //servo_angle = enc_count_servo * 0.3515625;
  packet.data.servo_enc = enc_count_servo;
  
  // * pi * d / 88 encoder count per one tire rotation (velocity in m/s)* convert from microseconds
  //velocity = -1 * (enc_count_motor - enc_count_motor_prev) * 3.1415196535 * 0.065 / 88 /(curr_dt-prev_dt)*1000000;
  packet.data.motor_enc = enc_count_motor - enc_count_motor_prev;
  packet.data.d_t = packet.data.curr_dt - prev_dt;
  prev_dt = packet.data.curr_dt;
  //prev_vel = velocity;
  enc_count_motor_prev = enc_count_motor;
//  int sendMessage = 0;
//
//  if(Serial.available() > 0){
//      sendMessage = Serial.read();
//  }

  //packet.data.curr_dt = 0xDEADBEEF;
  packet.data.servo_enc = 0xFEED;
  packet.data.motor_enc = 0xABBA;
  packet.data.d_t = 0xCAFEFACE;

//  // Send Count
  if (debugging == true) {
    Serial.println();
    Serial.print("Count: ");
    //Serial.print(packet.data.count);
    Serial.print(" ,Time: ");
    //Serial.print(curr_dt/1000000);
    Serial.print(packet.data.curr_dt);
    Serial.print(" , d_t:");
    Serial.print(packet.data.d_t);
    //Serial.print(" Servo angle: ");
    //Serial.print(servo_angle);
    Serial.print(" d_servo_encoder: ");
    //Serial.print(packet.servo_encoder);
    //Serial.print(" velocity: ");
    //Serial.print(velocity);
    Serial.print(" , motor encoder: ");
    Serial.print(packet.data.motor_enc);
    //cnt += cnt;
  } else {
    if(Serial.availableForWrite()){
        Serial.write(packet.buffer, sizeof(union ARC_Union));
    }
    delay(4);
  }
  //packet.data.count++;
}

There's still some dead code in there, I already chastised my project partner for trying to do floating point math on the Arduino with no FPU for timing critical applications.

Here is some of the C code running on the jetson xavier:

    uint32_t i = 0;
    uint8_t headCount = 0;


    printf("Making bigBuff\n");
    uint8_t bigBuff[20480];

    while(i < sizeof(bigBuff)){
        n = read(fd, &bigBuff[i], 1);
        if(n > 0){
            ++i;
            printf(".");
        }
    }

    printf("\n");

    int fd2 = open("test.bin", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);

    if(fd2 > 0){
        printf("success fd2: %ld\n", fd2);
        m = write(fd2, &bigBuff, sizeof(bigBuff));
        close(fd2);
    } else {
        printf("fail fd2: %ld\n", fd2);
    }

Fairly mundane. There is something really annoying going on with the arduino code.

If I hard set all of the values so I can have something like 0xDEADBEEF for the packet.data.current_dt. The arduino code never sends anything over UART telling me something in the loop is completely optimized out since no values ever change.

here is what I'm seeing in the hex editor:

This is all very frustrating and annoying. I'm thinking of just trying the uart pins since this USB UART has issues.

Are you serious? Please look at the specs of the Nano Every.

Bruh that's not on the nano it's the receiving linux code.

My mistake, sorry.

Not sure why I thougfht it was on the Nano. fd was the give-awy.

I just think whatever the nano every is using for USB to serial is flakey, or there's some interference?

I started reading the ratio between fully received packets and packet miss alignment detected. I get like a 0.024 ratio for a while then the UART freaks out and goes to 0.2+ ratio then it comes back and the ratio goes back down. It's totally weird.

I still don't understand why the nano doesn't send anything if I just hard set all the values in the struct, my only assumption is the compiler is interpreting the loop function as not doing anything and optimizing it out.

I want to try the physical UART pins next but my partner got over ambitious and mounted the whole thing into a custom 3D printed housing before us actually fully testing everything.

Now I have to dismantle half the car.

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