Serial.print time doesn't change with baud rate?

Hello, I'm trying to measure the time it takes for me to send some data to the serial monitor, but apparently this time doesn't change when I increase the baud rate at Serial.begin. Shouldn't it be faster when I increase baud rate?

Here is my test code: (I'm using arduino UNO)

// Variables for results storage
unsigned long tempo_inicio;
unsigned long tempo_fim2;

unsigned long valor = 1023;

void setup() {
Serial.begin(9600); //begins serial communication
}

void loop() {
// leitura
tempo_inicio = micros();
Serial.println(valor);
tempo_fim2 = micros();
Serial.print((tempo_fim2 - tempo_inicio));
Serial.println(" us");
delay(500);
}

I'm getting about 204 us and it doesn't change when I increase the baud rate. Can someone explain why this is happening and what can I do to make it faster?

If anyone wants detais, I'm doing this because I want to get data from the ADC and print it so I can copy and use it later, but the printing time will affect my sampling rate (it will be equal to the actual sampling time + printing time) and with 204 us to print the data my sampling rate will decrease a lot (about 3 times).

Regardless of the reason, just the fact that the Serial.println() time isn't changing with the baud rate is really weird to me.

Any help is welcomed. Thanks in advance.

Serial.print() just puts the data into the serial output buffer, so that is probably what you are measuring. The data is actually sent in the background and it is at that stage that the baud rate comes into play.

The serial output buffer can only hold 64 bytes so you may be able to get a sense of the affect of the baud rate if you deliberatly send messages that are significantly bigger than 64 bytes. When the buffer gets full Serial.write() waits until there is more space for the rest of the message.

The poorly named Serial.flush() waits until the output buffer is emptied. It could also be used as part of your time test process.

...R

Uhm... I added a Serial.flush() after the Serial.println() and got a time of over 6000us with 9600 baud rate and about 600us with 115200 baud rate so I think I understand now what you've said.

But that leads me to another question (also to see if I've fully understood): Does this mean that it's taking about 204us just to put the data in the serial output buffer?

If this is true, then it limits a lot my sampling rate (if I want to get the data available on the monitor to copy it later) :confused: isn't there a way to accelerate this process?

I get much smaller values if I use Serial.write() but then I don't get a readable output...

This is getting weirder....

My code:

// Variáveis para armazenar os resultados
unsigned long tempo_inicio;
unsigned long tempo_fim2;

unsigned long valor = 1023;
String str = String(valor);

void setup() {
Serial.begin(115200); //inicia a comunicação serial
}

void loop() {
// leitura
tempo_inicio = micros(); //marca tempo de inicio de leitura
//str = String(valor);
Serial.println(str);
//Serial.println();
tempo_fim2 = micros(); //le tempo somado da conversão + envio serial
Serial.print((tempo_fim2 - tempo_inicio));
Serial.println(" us");
delay(500);
}

I've converted the long to a String and got a smaller time difference between the micros() calls, does it mean that loading this string into the serial output buffer is faster than loading the long?

Also, if I remove the delay(500) at the end, the time between the micros() calls goes up to aroun 508us (it's low at the beggining and then it reaches this steady state of 508us and it's the same wheter I print the long or the string). My guess is that it has something to do with the fact that using the delay() I would get an empty output buffer everytime, but without the delay() I get to a bottleneck point where I have to wait for data to be released before I can add more in the buffer (am I going the right direction here?)

If all of this is true, then does it mean that my actual sampling period will be 508us + ADC time?

I'm really need some help here.

I've converted the long to a String and got a smaller time difference between the micros() calls, does it mean that loading this string into the serial output buffer is faster than loading the long?

Yes, because you print the long, that means it is not transfered as a long but converted to a string before being sent. In the last example you convert the long to a String object at the start of the sketch and use that one afterwards so the conversion doesn't have to be done again.

Also, if I remove the delay(500) at the end, the time between the micros() calls goes up to aroun 508us (it's low at the beggining and then it reaches this steady state of 508us and it's the same wheter I print the long or the string). My guess is that it has something to do with the fact that using the delay() I would get an empty output buffer everytime, but without the delay() I get to a bottleneck point where I have to wait for data to be released before I can add more in the buffer (am I going the right direction here?)

Correct, when the buffer is full the code waits until it gets some space again.

If all of this is true, then does it mean that my actual sampling period will be 508us + ADC time?

No, this is just the time for the serial handling and has nothing to do with a sampling time.

If all of this is true, then does it mean that my actual sampling period will be 508us + ADC time?

I'm saying this because I will sample using analogRead(), then print the data on the monitor before doing the next analogRead(), so the time between reads (which efectively dictates my sampling rate) will be the actual A/D convertion time (the analogRead()) plus the Serial.print() time.

Yes, because you print the long, that means it is not transfered as a long but converted to a string before being sent. In the last example you convert the long to a String object at the start of the sketch and use that one afterwards so the conversion doesn't have to be done again.

But when I convert right before the print, even though the time increases, it goes to 160us instead of the 204us that I get priting the long directly. Either way, if I take the delay() out the time for oth approaches is the same.

So, any ideas on how to make my prints take less time? T-T
Or maybe how can I obtain these data (the sampled values) in a readable and usable way (so I can use it to calculate a fft() on MATLAB for instance) without using Serial.print()?

Depending how you determine your sampling interval, you may want to keep track of the ‘time’ yourself, and defer the next serial transmission until just after a sample is complete.

If your sampling interval is really too short, you may need ‘a bigger boat’.

Interrupts may help, but you’ve already started to think it through - just need to go a bit further.

Sorry, I don't get it ._.

It is the business of converting the long to a string that is slow - whether you do it separately, or do it implicitly with Serial.print()

It would help if you explain your requirement - for example if there was no constraint how often do you want to call analogRead(). And how many samples do you need to take? For example you could take maybe 100 or 200 samples quickly and then pause while sending the data over the serial connection.

If you are sending the data to a PC program (i.e. not to the Arduino Serial Monitor) you should have no problem using 500,000 baud.

Also, analogRead() gives an int value, not a long - which would reduce the conversion time.

If you are sending data to a PC program that you are writing then you could send the data in binary format using Serial.write() in which case an int is just 2 bytes.

...R

I'm sampling a microphone with a range up to 15 kHz, so I need a Fs of 30 kHz or above.

Since the ADC is 10bits I will use an int, already changed that in my sketch and I'm using 1000000 baud rate and a prescaler of 16 for the ADC (instead of the 128 prescaler which is the arduino's default). So I'm doing all I can to maximize my rate, still the best I get is around 180us between each sample, so my Fs is way below 30 kHz. The bottleneck really seems to be in the printing, but I don't know a better way to do it and I don't know how to write a program so I can send data to it instead of sending it to the serial monitor :confused:

I've tested Serial.write() and it is indeed faster (even though I don't know if fast enough), but at the serial monitor the data isn't readable this way (it prints nothing, sometimes a square, stuff like that) so I can't copy it and use it later.

If I fill up an array with data before pausing to send it all, there would be 2 problems:

1 - SRAM is only 2kB in arduino uno, so at 30 kHz (if I managed to get there) this would be way less than a second of audio.

2 - Pausing would make breaks, cuts in the audio. If I could pause after maybe 5 or 10 seconds of data aquisition it might be ok, but because of the 2kB limit I would have to pause several times per second.

It is the business of converting the long to a string that is slow - whether you do it separately, or do it implicitly with Serial.print()

I don't think that's all, because in one of the code I sent, this conversion was done just once and then the data was just being directly printed as a string (it wasn't converting from long to string every iteration of the loop).

If we can get such high sampling rates (considering just the A/D conversion) as over 50 kS/s (using the 16 prescaler) it seems unlogical to me that a serial transmission with a 1000000 baud rate would drop my pratical Fs below 30 kHz.

Still, I don't know what to do to configure serial to be faster and legible. :confused:

An at328 will only sample at up to about 10kHz at 10 bits , and the overhead of sending this 10 bits ( 2 bytes)
per sample over a serial link implies about 200kB/s.

Doing nothing else.

And the arduino environment has significant overheads - there are chunks of time used for background utilities.

And 10 bits / 10ks/s is an awful long way from quality audio

I suggest you choose a faster and higher resolution platform. Decent resolution ( 16+bits) will not be acheivable with any general purpose processor - you need special hardware for that.

Allan

I'm not really looking to reproduce the audio or anything, so resolution is not a must (I don't need much quality) but at least perform an fft() in MATLAB later. I've done it for a 1 kHz sine wave, but for a 15 kHz one I need at least the 30 kHz Fs.

Why not use the audio input on your PC?

MUCH more speed and resolution!

There's loads of free software to acheive what you want.

Allan

I can't, this is part of a bigger environment and we're going with arduino for now. There won't be a PC later, I'm just using it now so I can test some characteristics of the system. I'll have some more trouble later on because I'll have to send this data over wifi.

Regardless, the question is whether I can speed up the serial print or do some work around here (but using arduino) and how to do it. I think it's an interesting question per se and there's nice things to be learned here. The reason behind why I'm doing this doesn't invalidate the question, even if there are clever ways to do it without using an arduino :confused:

I thought about something. Is there a way I could create a file (say a .txt or .dat) and write the data to it instead of the serial monitor? This way I could still have it available for use later. I wonder if this method would be faster or would have the same problem as the serial monitor (or be even slower) .

siridakis:
I can't, this is part of a bigger environment and we're going with arduino for now. There won't be a PC later,

Why not? It's not as if a cheap laptop is expensive? And they come with screen, keyboard and uninterruptible power supply as standard.

And if small size is important then use a RaspberryPi.

Arduinos are great, but that does not mean they can do everything.

...R

If you have an SD card on your Arduino, you can use that to save a file and later transfer it. I suspect that your sampling will slow down at the moment that the software buffer is filled and it's written to the card.

You can speed up the process by using an interrupt driven analogRead() equivalent; see e.g. Interrupt-Driven Analog Conversion With an ATMega328p. That way you can send data to the PC while taking your next analog sample; I'll leave the calculations how much you can gain up to you :wink: And write raw data, not converted to text; 2 bytes versus max 4 (or 5) bytes, you just have to figure out how to deal with raw data in MatLab.

If you have an SD card on your Arduino, you can use that to save a file and later transfer it.

Thank you, I'll check this option. I've done some SD card writing before (about 3 years ago, so I have to catch up) but I was formatting the data in FAT32 and it took a while to write. Using raw data I wouldn't need to do that? Would the SD card still be readable on a PC?

I remember I used SPI to write on the card, so I'll have to check if the SPI speed won't be a bottleneck like the serial is being right now. But it's an idea :slight_smile: and I'll be sure to check out the link you suggested, thanks for that. I'm even considering writing a sketch to read the raw data stored on the card and then print it on the serial monitor, because time won't be a problem then (if I manage to store the data on the card at the desired rate).

It will probably take me some time to study and test this approach, but I'll be sure to report here what I get.

Arduinos are great, but that does not mean they can do everything.

I know arduinos can't do everything, but I'm interested in the limits I can reach, even if it's to justify that I can't do what I want using an arduino. This is something I'm doing for academic purposes, so I want to at least be able to draw the line of how far can the arduino take me, instead of just going with another option without knowing what my limits were.

Why not? It's not as if a cheap laptop is expensive?

Where I am a cheap laptop is over 10x what I pay on an arduino.
I might go with different approaches later, but as I said, this has academic purposes and I want to be able to document the limits of what I can do using the arduino first.

I'm sampling a microphone with a range up to 15 kHz, so I need a Fs of 30 kHz or above.

Consider using an I2S microphone with CPU with I2S hardware controller. I2S microphones are used in cellphones so they are cheap. I2S DAC with speaker amplifier boards are also easy to find. There are I2S breakout boards that cost less than $10.

  1. Arduino boards based on SAMD have I2S hardware.

  2. Google for "pjrc teensy audio". Real-time audio processing with a fast ARM Cortex processor. Really cool graphic language front end/code generator.

  3. Or if you need more horsepower Raspberry Pi, GHz CPU speeds, quad core, WiFi, Bluetooth, google for "i2s microphone raspberry pi". Pi Zero W is probably good enough but Pi3 is cheap compared to a laptop.

  4. Even ESP8266 has an I2S controller but I have only seen it used with an I2S DAC.

Even if you sample at 30kHz , and expect to encode 15kHz accurately, you're in for a surprise.

1/ at 15khz you're only getting 2 samples per sinewave - suppose those sampling points coincide with zero-crossings rather than peaks? Unpredictable. Hence oversampling (many times higher than Fin max x 2) is considered a good idea, and is the basis of modern audio a/d's

2/ Suppose some 16kHz is present as well? - that will 'wrap around' to appear as if it were 1kHz. This is called 'aliasing'. Hence you need an extremely sharp analogue filter to remove any frequencies above 1/2 your sampling rate. Oversampling helps a lot here as well.

Can be done - but not easy and certainly not with a 16MHz AT328 platform.

Allan

siridakis:
Using raw data I wouldn't need to do that? Would the SD card still be readable on a PC?

When I spoke about writing raw data, I meant writing integers (2 bytes per reading), not text.