Serial1 stuttering during delivery

Hey all,

I’m having some issues with the Serial1 peripheral.
I have an application which has pretty tight timings, specifically modbus.
It specifies the end-of-frame as 3.5 character periods. At 9600 baud that means each byte takes about 1.2 ms, so a T35 period is 4.8 or 5 ms.

We’ve been doing this for a while, but recently I’ve found something breaking when attempting to integrate with a new sensor (which might implement the spec a bit more strictly!). It seems that Serial1 is delivering information in bursts, with blank periods between them.

I’ve written a minimal reproduction, which I connected via a RS-485 converter to qmodbus (you should be able to use a USB-Serial converter. It shows the data being delivered, but it’s split into modbus frames (seems to be splitting at 5 bytes).
The digital writes are in-place to activate other peripheral hardware. They aren’t necessary, but I figured I’d keep them in for completeness.

Anyone have any insight into this? I’ve tried single threaded and atomic blocks to no avail. I’m using Device OS 0.6.4

#include <application.h>
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);
void setup()
{
        Serial.begin();
        Serial1.begin(9600, SERIAL_8N1);
        pinMode(A5, OUTPUT);
        digitalWrite(A5, HIGH);
        Serial.println("Started");
}

void loop()
{
        Serial.println("Sending");
        uint8_t data[] = { 0x02, 0x03, 0x00, 0x0d, 0x00, 0x02, 0x55, 0xfb};
        delay(1000);
        SINGLE_THREADED_BLOCK() {
        digitalWrite(A5, LOW);
        delay(10);
        while(Serial1.available()) { Serial1.read(); }
        Serial1.flush();
        for(int i = 0; i < 8; ++i)
        {
                Serial1.write(data[i]);
        }
        
        Serial1.flush();
        delay(5);
        digitalWrite(A5, HIGH);
        }
        delay(1000);
}

If timing is critical you should not use the byte-wise write but go with the bulk version

  Serial1.write(data, sizeof(data));

Having delay() in a SINGLE_THREADED_BLOCK is a bad idea and I don’t see any need for that in your case anyway.

I’ve tested this code with Serial1.write(data, sizeof(data)/sizeof(uint8_t)).
However the performance is identical.
I notice that the stutter seems to be occuring after 5 bytes are sent, fairly consistently.
I’m trying to verify it’s not my tools, but using x86 modbus software with the same USB connectors and wiring works like a charm with an identical byte array.

I’ll remove the delay calls. Does anyone else have this time smearing or is it limited to my device?

Just some notes

  • sizeof(uint8_t) is always and on any platform 1 (that’s the 8 indicating 8 bit)
  • you can collaps while(Serial1.available()) { Serial1.read(); } into this while(Serial1.read() >= 0); for more speed as it will use fewer function calls
  • Serial1.flush() does only wait for the TX buffer to become empty, so your first instance (after the initial delay(1000)) is superfluous as there won’t be any residual bytes in the buffer after 1+ seconds and the delay after the second instance is also superfluous as all data has already been sent
  • Serial1.write() will only ever place your data into the TX buffer, the actual transfer is handled by the HW interface, so writing the data in bulk will always be the better option as it places all data into the buffer as fast as possible preventing the HW interface from (potentially) needing to spin down and back up after each transferred byte (especially when using higher baudrates)
2 Likes

I moved the extra flushes, and kept the Serial1 commands in SINGLE_THREADED_BLOCK()s. Moving the appropriate delays outside seemed to help.

I’m still seeing skew on the usb-serial converter, but the actual sensor is tolerant to the timing.
It may be that the sensor has better termination on the transmission line than the usb device.

Thanks for the advice.