Measuring and Improving Serial Performance / Serial.flush() Blocks Forever on Nano 33 BLE Sense

To gauge the bytes/s data transmission capabilities of the Nano 33 BLE Sense's USB CDC Virtual Serial COM I am trying to run this code from an older post on this forum which was originally used on the Leonardo:

const unsigned long NUMERATOR = 1000000000;

void setup(){
  Serial.begin(115200); //Does nothing on the nano 33 ble sense.
  while (!Serial); //Wait for serial port to connect. Needed for native USB on nano 33 ble sense.
  Serial.println("Begin printing.");
}
  
void loop(){
  Serial.println("Enter loop.");
  
  unsigned long startClock = micros();
  for (int i = 1000; i > 0; i--) {
    Serial.write('.'); //Non-blocking so wait until complete using Serial.flush() directly after...
    Serial.flush(); //Blocks forever on nano 33 ble sense.
  }
  unsigned long endClock = micros();
  
  uint32_t bytesPerSecond = NUMERATOR / (endClock-startClock);
  Serial.println("");
  Serial.print(bytesPerSecond);
  Serial.println(" bytes/second");

  while(1);
}

The problem is the Serial.flush() line used to wait on the Serial.write('.') to complete. Is this a known bug? Is there an alternative to Serial.flush() that I can use on the Nano 33 BLE Sense or some other strategy to test the data transmission rate?

This was fixed by replacing UART::flush() with the following:

void UART::flush() {
#if defined(SERIAL_CDC)
	if (is_usb) {
		while(!_SerialUSB.writeable());
	} else {
		while(!_serial->obj->writeable());
	}
#else
	while(!_serial->obj->writeable());
#endif
}

In the file 'cores/arduino/Serial.cpp'.

isn't that what is in the current build?

(writeable() returns 1 always)

Not in the release version:

ok - I was looking there

Yeah, the fix is already merged in and will be in the next release. It sounds like that will be soon to come:
https://github.com/arduino/ArduinoCore-mbed/issues/357#issuecomment-958987118

facchinm commented 9 days ago

The plan was re-releasing this week but we are waiting a bit more to merge some more goodies, so next week the change will likely be live

Until then, it is necessary to manually apply the fix if you are using the release version that is available via Boards Manager.

MODERATOR EDIT

Continuing the discussion from Serial.flush() blocks forever on Nano 33 BLE Sense:

Note: Please follow the steps outlined in linked discussion if you would like to run the following sketch yourself. The current Arduino release implements these changes but we can't download it from Board Manager as of the time of writing this...

So I managed to get Serial.flush working but I am disappointed with the data transfer rates I am recording:

const byte BYTE_TO_SEND = 170; //b'10101010'.
const unsigned long NUMERATOR = 1000000000;

void setup(){
  Serial.begin(115200); //Does nothing on the nano 33 ble sense.
  while (!Serial); //Wait for serial port to connect. Needed for native USB on nano 33 ble sense.
}
  
void loop(){
  unsigned long startClock = micros();
  for (int i = 1000; i > 0; i--) {
    //Serial.write(BYTE_TO_SEND); //11199 bytes/second on nano33blesense.
    Serial.write(BYTE_TO_SEND); //19435-19620 bytes/second on portenta h7.
    Serial.flush();
  }
  unsigned long endClock = micros();
  
  unsigned long bytesPerSecond = NUMERATOR / (endClock-startClock);
  Serial.println("");
  Serial.print(bytesPerSecond);
  Serial.println(" bytes/second");
  while(1);
}

11199 bytes/s on the Nano 33 BLE Sense and, just because I was curious, 19620 bytes/s on the Portenta H7.

The guys from this older post who did a similar speed test with the Arduino Leonardo measured 39258 bytes/s! Any ideas on how I can get as fast, or hopefully, faster rates?

have you tried this which would be more representative of bulk data transfer?

  unsigned long startClock = micros();
  for (int i = 1000; i > 0; i--) Serial.write(BYTE_TO_SEND);
  Serial.flush();
  unsigned long endClock = micros();

(and you have the cost of the for loop - so you might want to transfer 1000 bytes from an array (or random location) in memory) - may be it's more efficient at iterating

const byte BYTE_TO_SEND = 170; //b'10101010'.
const unsigned long NUMERATOR = 1000000000;

byte buffer[1000];

void setup() {
  Serial.begin(115200); //Does nothing on the nano 33 ble sense.
  while (!Serial); //Wait for serial port to connect. Needed for native USB on nano 33 ble sense.
  memset(buffer, sizeof buffer, BYTE_TO_SEND);

  unsigned long startClock = micros();
  Serial.write(buffer, sizeof buffer);
  Serial.flush();
  unsigned long endClock = micros();

  unsigned long bytesPerSecond = NUMERATOR / (endClock - startClock);
  Serial.println();
  Serial.print(bytesPerSecond);
  Serial.println(" bytes/second");
}

void loop() {}

Topics on the same subject merged

Please do NOT cross post / duplicate as it wastes peoples time and efforts to have more than one post for a single topic.

Continued cross posting could result in a time out from the forum.

Could you also take a few moments to Learn How To Use The Forum.

Other general help and troubleshooting advice can be found here.
It will help you get the best out of the forum in the future.

@UKHeliBob
I don't understand why these two topics were merged, if you would read both of them you would see that one is describing a bug which I didn't know how to fix, and the other is asking why my measured data speeds are so slow.

Please consider undoing the merge as where I intend to take this discussion will appear to be off-topic to future readers.

Also have a good day (:

Running your code, I got 16129032 bytes/s and 439174 bytes/s on the Portenta and Nano, resp..

These values sound kind of ridiculous now though... Could you confirm I'm doing the math right still?

not really. high-speed USB communication (USB 2.0) goes up to 480 Mbps

the math should be:

you transferred 1000 bytes in ((endClock - startClock) / 106 ) seconds
so in 1 second you transferred 109 / (endClock - startClock) bytes

which seems to be what you have

To get to bps you would need to multiply this by 8 (8 bits per byte) but there are also more stuff being transferred in the protocol, so actually more bits are really pushed out.

PS: @UKHeliBob
I would tend to agree with @lsi8 on this

topics were related but different questions and now the title seems to say the discussion is about the bug, whilst it's about data transfer speed. The reference to the previous discussion was a helpful hint for those willing to duplicate the test that they needed to alter the current build to include a patched flush() version.

1 Like

Says it all for me

as stated, related but two different questions in my opinion.

question 1: why the hell is flush not working on the Nano 33 BLE Sense?
question 2: how fast can USB CDC be and how do you test / measure?

seems very different to me

The "Continuing the discussion from..." part was added in automatically by the site when I hit the "link to previous discussion" button.

It was the

that was the deciding factor in me merging the two topics as it requires the reader to go to another topic to see what code the second topic refers to

@UKHeliBob
Why would this website give the option to link to another Arduino Forum topic if you are saying we have to just reiterate everything that was concluded in said linked topic?

This prohibition only makes sense if we operate under the premise that past topics on this site might be lost but that isn't the case, correct?

For the record, I do operate under this premise when linking to content from external sites, and always try to summarize said content, to be considerate to future readers.

You are not being considerate right now by veering this topic further and further away from the intended goal.

So the first tests I did were on an older Windows PC with probably USB 1.0 or USB 2.0 ports.

I tried doing the same two tests but on my newer MacBook Pro (2019 I think) and got these results:

const byte BYTE_TO_SEND = 170; //b'10101010'.
const unsigned long NUMERATOR = 1000000000;

void setup(){
  Serial.begin(115200); //Does nothing on the nano 33 ble sense.
  while (!Serial); //Wait for serial port to connect. Needed for native USB on nano 33 ble sense.
}
  
void loop(){
  unsigned long startClock = micros();
  for (int i = 1000; i > 0; i--) {
    //Serial.write(BYTE_TO_SEND); //11199 bytes/s on nano33blesense PC.
    //Serial.write(BYTE_TO_SEND); //19435-19620 bytes/s on portenta h7 PC.
    //Serial.write(BYTE_TO_SEND); //9818-10550 bytes/s on nano33blesense Mac.
    Serial.write(BYTE_TO_SEND); //36047-37163 bytes/s on portenta h7 Mac.
    Serial.flush();
  }
  unsigned long endClock = micros();
  
  unsigned long bytesPerSecond = NUMERATOR / (endClock-startClock);
  Serial.println("");
  Serial.print(bytesPerSecond);
  Serial.println(" bytes/second");
  while(1);
}

Roughly the same for the Nano33; I'm assuming because I had to connect the micro-USBA cable through a thunderbolt hub which is running my display, speakers, usb mouse, keyboard, etc..

However for the Portenta, I think because I was able to plug the USBC-USBC cable directly into the MacBook's thunderbolt port, I received 37163 bytes/s vs. the 19620 bytes/s on the PC!!!

There are several things that the forum software allows but it does not always mean that they are a good idea. In your circumstances I feel that it would have been better to have posted the code that you were referring to as part of your initial post in the new topic

As to

With respect, it is not me that is veering the topic away from its intended goal. By merging the two topics, anyone reading the combined topic will have the full facts as to the original query, its solution and the relevant code all in one place. Don't forget that many users access the forum on mobile devices with small screens so having 2 browser windows open at once is not always practical

sure, but the core of the discussion was measuring performance if I got that right. Now the title of the topic does not do it justice.

and if we change it to that, then someone looking at fixing flush() won't find it easily

that's why I think 2 topics were better than one.