T4.1 fast print for USB, Serial and SD

Status
Not open for further replies.

RayanR

Member
Hy,

i'm working on a sensor system that runs at 50 Hz. As such, i'd like to log these sensors onto an SD card at 50Hz aswell. However, i'd like to write the same data at 2Hz to both the USB port and a Serial port (#5) for both wired and wireless monitoring.

However printing all the sensor data (41 different values, floats and ints) 3x using the following code takes a lot of time as the data is converted every time. I tried writing to a buffer using sprintf to format the data and then writing this string to the 3 ports, but this made the system really slow.

Code:
void SerialTerminal_update(Stream &port) {
  SerialTerminal_COMMON(port); port.print(" | ");
  SerialTerminal_VI(port); port.print(" | ");
  SerialTerminal_RC(port); port.print(" | ");
  SerialTerminal_BME(port); port.print(" | ");
  SerialTerminal_BNO(port); port.print(" | ");
  SerialTerminal_Lidar(port);  port.println();*/
}
void SerialTerminal_COMMON(Stream &port) {
  port.print(airspeed_value); port.print(" , ");
  port.print(rpm_value);
}
void SerialTerminal_VI(Stream &port) {
  port.print(v_supply); port.print(" , ");
  port.print(v_mot); port.print(" , ");
  port.print(v_solar); port.print(" , ");
  port.print(i_mot); port.print(" , ");
  port.print(i_solar); port.print(" , ");
  port.print(used_mah_since_arm); port.print(" , ");
  port.print(charged_mah_since_arm); port.print(" , ");
  port.print(used_mWh_since_arm); port.print(" , ");
  port.print(charged_mWh_since_arm);
}
void SerialTerminal_RC(Stream &port) {
  port.print(RC_Roll);        port.print(" , ");
  port.print(RC_Pitch);       port.print(" , ");
  port.print(RC_Throttle);    port.print(" , ");
  port.print(RC_Yaw);         port.print(" , ");
  port.print(RC_Flaps);       port.print(" , ");
  port.print(RC_FlightMode);  port.print(" , ");
  port.print(RC_Arm);         port.print(" , ");
  port.print(RC_RSSI);        port.print(" , ");
  port.print(RC_LostFrame);   port.print(" , ");
  port.print(RC_Failsafe);    port.print(" , ");
  port.print(RC_Trim_Roll);   port.print(" , ");
  port.print(RC_Trim_Pitch);   port.print(" , ");
  port.print(RC_Trim_Yaw);
}
void SerialTerminal_BME(Stream &port) {
  port.print(bme_temperature); port.print(" , ");
  port.print(bme_pressure); port.print(" , ");
  port.print(bme_altitude);
}
void SerialTerminal_BNO(Stream &port) {
  port.print(bno_temp); port.print(" , ");
  port.print(orient_pitch); port.print(" , ");
  port.print(orient_roll); port.print(" , ");
  port.print(orient_yaw); port.print(" , ");
  port.print(acc_forward); port.print(" , ");
  port.print(acc_side); port.print(" , ");
  port.print(acc_up); port.print(" , ");
  port.print(comp_forward); port.print(" , ");
  port.print(comp_side); port.print(" , ");
  port.print(comp_up); port.print(" , ");
  port.print(gyro_pitch); port.print(" , ");
  port.print(gyro_roll); port.print(" , ");
  port.print(gyro_yaw);
}
void SerialTerminal_Lidar(Stream &port) {
  port.print(lidar_dist);
}

I was wondering if the following:
- Is there a way to link the USB Serial and Serial 5 to each other, as i'm writing the same data to them.
- how can i make it format only once and then send 3 times?

Thnx,
RR
 
I guess the Hardware Serial takes the most of the time here. Which Speed do you use?

Post a complete program, next time.
(Forum rule)
 
hy, yeah i forgot to add the file as it's pretty big, here it is: View attachment 24916

No.. nobody looks at a big file, in a forum. At least, i know of nobody.
Strip it down to the minimum that shows the problem, if you want anybody to look at it..

I'm using 115200 for USB and 57600 for Serial 5 as the latter speed is what's used for the Holybro transiever that i'm using.

The "115200" for USB get ignored anyway. It is always full usb speed, so it does not matter. U could use "100" it would still be full USB speed.

57600: Do the math.
10 Bits per transfered byte.

10 * (1/57600) = ~174us. Multiply this by the # of bytes.
You'll get a number that is way higher than the teensy needs to calculate things.
But it has to wait for the serial interface.
 
Last edited:
Your code with multiple "port.print(...)" statement is very inefficient for the serial port. Each call will probably wait until it is finished to start the next statement. It should be much more efficient to use sprintf to make up a string, then send the string in a single transmission to the two ports. You could also do it with a port.printf statement, or series of statements, each of which sends several variables. Another advantage of printf() is that you control the formatting and don't have to accept the default format of print().

A few other things to consider:

1. Send the data to the USB port first---it will go out very quickly as long as the program on the other end can suck up the data as fast as you are sending it. USB serial is very efficient at buffering up the data and sending it in the background using DMA and interrupts.

2. Send the data to the serial port last. It is going to take much longer, no matter how well you buffer and format it. However, the more you can send in a single printf() statement, the fewer calls to the serial driver. If you manage it properly, you can be back to your main program while the serial driver spends many milliseconds transmitting the data. At 57600 baud, an 80-character output line takes about 14milliseconds to send.

3. When you want to go pro on this kind of stuff, learn how to build binary data structures for storage and transmission. You cut the storage and bandwidth needed by a factor of ~2 to 3 ("123.456 , -789.23" goes from 16 bytes to 8) as the program at the receiver inserts the comma and spaces. You also get the advantage that you can reformat the printout in the receiver to use the full resolution of the data or limit it to the number of significant figures dictated by your sensor resolution and noise level. If you're going to use the data in calculations or plots, you also won't have to re-convert from ASCII to binary.
 
No.. nobody looks at a big file, in a forum. At least, i know of nobody.
Strip it down to the minimum that shows the problem, if you want anybody to look at it..
.
well, thats the thing. it doesn't give a problem when not having the full code.


I stripped out the USB, as it was not really needed and went to a 2Hz interval to write to the serial port. And now it works perfectly. i came to a calculation of about 80ms to transfer things, so i have another 420 to do the rest.
 
I would recommend going back to the formatting to a buffer, then sending the buffer method. USB is a packet protocol, and it is more efficient to send a few large packets than lots of small ones.

For the serial ports, Teensy extends the Serial object with the addMemoryForWrite() and addMemoryForRead() methods. My best guess says that your prints are blocking waiting for the transmit buffers to become available. If you have large enough transmit buffers the sending should be performed at interrupt level without blocking the user level code.

Patrick
 
2. Send the data to the serial port last. It is going to take much longer, no matter how well you buffer and format it. However, the more you can send in a single printf() statement, the fewer calls to the serial driver. If you manage it properly, you can be back to your main program while the serial driver spends many milliseconds transmitting the data. At 57600 baud, an 80-character output line takes about 14milliseconds to send.

How would i manage this properly? I already went through and took away the spaces, as these were only good for readability, not performance :p
When you want to go pro on this kind of stuff, learn how to build binary data structures for storage and transmission. You cut the storage and bandwidth needed by a factor of ~2 to 3 ("123.456 , -789.23" goes from 16 bytes to 8) as the program at the receiver inserts the comma and spaces. You also get the advantage that you can reformat the printout in the receiver to use the full resolution of the data or limit it to the number of significant figures dictated by your sensor resolution and noise level. If you're going to use the data in calculations or plots, you also won't have to re-convert from ASCII to binary.
For future versions, i'd love to do this. but sadly, i don't have the time for it now.
 
Status
Not open for further replies.
Back
Top