As you probably know, SoftwareSerial uses Interrupts to do some of the timing stuff of the Serial communication.

What I am trying to accomplish is to measure the time that it takes SoftwareSerial.write() to write/send one byte.

Obviously, I started trying with timestamps based on millis(), but found out that SoftwareSerial messes with the millis() timer and it simply doesn't count properly.

I have something like this:

timestamp = millis();
HC12.write(endByte); //HC12 is a SoftwareSerial object

//After sending waits for the answer
while (HC12.available() > 0) {
  byte r = HC12.read();
  if (r == (byte) 254) { //The other HC12 module sends 0xFE
    unsigned long received = millis() - timestamp;

Any suggestion on how I could measure this?

I am trying to implement a half-duplex communication with HC12 433MHz modules and I am testing some theoretical/real timing values for the communication between two boards.

  • the time is dependent on the baud rate (bits/second) ..... at least 10 bits are sent .... one start bit, 8 data bits and one stop bit ........ the stop bit has to be at least one bit-length, but it could be longer – jsotola Jan 28 at 18:15
  • @jsotola, Hi. Thanks for your help. Well, I know :-). The thing is, that's theoretical. Also, I want to try with more bytes and I don't think that the timings are exacly the same of calculations based on baud rate. – Fred Jan 28 at 18:17
  • actually the solution is quite simple ..... the transmitting arduino is actually stand-alone ..... there does not have to be a receiving arduino ........ connect the two arduinos together using one of the digital pins ...... the transmitting arduino sets the digital pin, sends software serial byte and clears the digital pin ...... the other arduino measures the time between setting and clearing of the pin .............. you could also time the software serial pin itself – jsotola Jan 28 at 18:46
  • try this .... github.com/gillham/logic_analyzer – jsotola Jan 28 at 18:58
  • @juraj, The snippet is now correct. What do you mean the title of the question doesn't match the question? – Fred Jan 28 at 19:06

It is very simple, and you don't need any code to work it out.

SoftwareSerial is completely synchronous (i.e., blocking) when writing. That means that the amount of time taken to peform the write is the time taken to send the data on the line plus a small function call overhead.

That means the time taken is:

1 / baud * 10 seconds

So at 9600 baud one bit is sent in 1/9600 of a second, and there are 10 bits (1 start, 8 data, and 1 stop bit), so it takes 1/960th of a second.

That's about 1ms per byte, plus a few extra clock cycles for calling the actual functions.

To put it in more detail, when you send a byte this happens:

  1. Set the line level to the start bit level
  2. Wait 1/9600th of a second
  3. Set the line level to the first data bit level
  4. Wait 1/9600th of a second
  5. Repeat 3&4 for the rest of the data bits
  6. Set the line level to the stop bit level
  7. Wait 1/9600th of a second
  8. Set the line level to the idle level

So you have 10 "wait 1/9600th of a second" in total, or a total time of 1/960th of a second.

The same can be said of receiving data. When the start bit arrives it triggers a pin change interrupt. A synchronous blocking interrupt routine then executes that does the same as above but with reading instead of writing. So that takes about 1ms to execute at 9600 baud - plus some extra to insert the incoming byte into a circular buffer, which you then read from in your sketch.

So the round-trip time of a single byte from unit A to unit B then from unit B to unit A can be estimated as:

1/960 + 1/960 + overheads

That means the minimum time will be 1/480 seconds plus whatever time is spent at the remote unit processing the byte to send data back to you, and a little bit for the function call overheads.

Incidentally, it's that last operation in the interrupt, the "adding the byte to a circular buffer" that means you can't reliably receive at 115200 baud with SoftwareSerial. That takes more time to process than the typical gap between bytes (could be zero - just the width of the stop bit...) that the next byte could have already started to arrive at the GPIO pin before the Arduino is capable of triggering the ISR again, so it misses the start bit.

  • Hi, Awesome explanation. Thanks for your time! – Fred Jan 29 at 1:47

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.