How accurate is DELAY function?

Here is a code made by Artificial Intelligence.

#include <Arduino.h>

byte data[] = {
  0x20, 0xDF, 0xFF, 0x61, 0x60, 0xFF, 0x69, 0x00,
  0xFF, 0x61, 0x60, 0x00, 0x68, 0x00, 0xA6, 0xEF,
  0x21, 0xDE, 0xFF, 0x61, 0x61, 0x57, 0x6A, 0x00,
  0xFF, 0x61, 0x61, 0xD5, 0x6A, 0xFF, 0xE8, 0x60,
  0x22, 0xDD, 0xFF, 0x61, 0x62, 0x00, 0x68, 0x00,
  0xFF, 0x00, 0x61, 0x6E, 0x48, 0x00, 0x25, 0x5F,
  0x23, 0xDC, 0xFF, 0x5E, 0x61, 0x64, 0x6A, 0x00,
  0xFF, 0x60, 0x5F, 0x00, 0x68, 0x9A, 0xA6, 0xEB,
  0x24, 0xDB, 0xFF, 0x61, 0x5E, 0x3E, 0x6A, 0x00,
  0xFF, 0x61, 0x5E, 0x26, 0x6A, 0x26, 0xA6, 0x79,
  0x25, 0xDA, 0xFF, 0x62, 0x60, 0x3E, 0x6A, 0x00,
  0xFF, 0x00, 0x62, 0x00, 0x48, 0x00, 0x25, 0x31
};

const int RSE_PIN = 10;
const unsigned long FRAME_DELAY_US = 1019;  // 573 (frame) + 446 (desired delay)
const unsigned long LOOP_DELAY_MS = 9;

const byte firstByteCycle[] = { 0x20, 0x26, 0x2C, 0x32, 0x38 };
byte cycleIndex = 0;

void setup() {
  pinMode(RSE_PIN, OUTPUT);
  digitalWrite(RSE_PIN, HIGH); // Always in transmit mode

  Serial1.begin(19200, SERIAL_8O1);  // 8 data bits, Odd parity, 1 stop bit
}

void loop() {
  // Update the first byte before each transmission loop
  data[0] = firstByteCycle[cycleIndex];
  cycleIndex = (cycleIndex + 1) % 5;

  for (byte b : data) {
    unsigned long t_start = micros();
    Serial1.write(b);
    
    // Wait until full frame duration (573 us) + 446 us inter-frame gap
    while (micros() - t_start < FRAME_DELAY_US) {
      // Busy wait to ensure spacing
    }
  }

  delay(LOOP_DELAY_MS);  // Wait before repeating the whole transmission
}

This code works as intended but FRAME_DELAY_US has slight deviation from what is in the code. It is alternating between X us after or Y us before set value. Is there any way to make it more precise? What is the hardware limit on timing?
Second problem is that I need more accurate delay than MS for "const unsigned long LOOP_DELAY_MS". But when i change it to "const unsigned long LOOP_DELAY_US = 9000" and at the end "delay(LOOP_DELAY_US);"; Program runs as if there was no loop delay at all. Googling "FRAME_DELAY" Doesn't show any documentation or instructions about this. I'd like to understand more words used in this particular code.

Thank you for advices in Advance :slightly_smiling_face:

delay() waits the number of ms you specify .

A while loop calling micros() has its own builtin cost (for fetching the value of micros() and performing the compare and the branch. if you want to pass the delay in µs then use delayMicroseconds(). As the doc states,

This function works very accurately between 3 and 16383 microseconds. We cannot assure that delayMicroseconds will perform precisely for smaller delay times. Larger delay times may result in a very brief delay

accuracy for delay or delayMicroseconds depends also on what happens with the interrupts as they are not disabled by default (UART, etc ...)

side note : calling Serial.write() has also a cost and you send the data at 19200 bauds. waiting for 1019 µs after that call does not guarantee that each byte will be actually separated by that exact duration. Can you share some more details about what are the timing requirements you really need? Is data the full Frame you want to send?

It is a start bit / 8 data bits / parity bit / stop bit.
frame spacing is (counting from end of stop bit to beginning of start bit) ~~8591-8593 microseconds. Byte spacing is ~~445,5 microseconds.
Forgot to add that I am using Arduino DUE. I am trying to get closest timing possible because so far receiving device gives some warnings but accepts the message anyway. Warnings might come from error checking unrelated to frame timing. So the first thing to enhance is timing. Yes, It's a full frame.

Regular serial communication doesn't need "perfect" timing and it usually works...

Without studying your code...

No clock/oscillator is perfect. The clock in the "basic' Uno or Mega is a ceramic oscillator that has more tolerance than a crystal. (I don't remember the specs.) Crystals typically have a tolerance of 100 or 200ppm, which is good but not as good as a clock or watch, which also use crystals but watches & clocks use "tricks" to tweak the accuracy.

Every "step" in your program takes some time. If you look at the Blink Example, ideally the LED should be on for one second and off for one second and the loop should repeat every 2 seconds. But, digitalWrite() statements take a few (or several) microseconds and the loop() command takes a bit of time so the loop runs a little slower than you might expect. and errors will accumulate as you run it for minutes, hours, days, etc.

The Blink Without Delay Example is not slowed-up by other steps/statements in your code but it can still be imperfect because it only checks millis() when the loop comes-around and checks again.

1 Like

so that's managed by

this is what defines the bit communication pattern

what you do is pause between bytes (/frames)

your loop could just be

byte data[] = {
  0x20, 0xDF, 0xFF, 0x61, 0x60, 0xFF, 0x69, 0x00,
  0xFF, 0x61, 0x60, 0x00, 0x68, 0x00, 0xA6, 0xEF,
  0x21, 0xDE, 0xFF, 0x61, 0x61, 0x57, 0x6A, 0x00,
  0xFF, 0x61, 0x61, 0xD5, 0x6A, 0xFF, 0xE8, 0x60,
  0x22, 0xDD, 0xFF, 0x61, 0x62, 0x00, 0x68, 0x00,
  0xFF, 0x00, 0x61, 0x6E, 0x48, 0x00, 0x25, 0x5F,
  0x23, 0xDC, 0xFF, 0x5E, 0x61, 0x64, 0x6A, 0x00,
  0xFF, 0x60, 0x5F, 0x00, 0x68, 0x9A, 0xA6, 0xEB,
  0x24, 0xDB, 0xFF, 0x61, 0x5E, 0x3E, 0x6A, 0x00,
  0xFF, 0x61, 0x5E, 0x26, 0x6A, 0x26, 0xA6, 0x79,
  0x25, 0xDA, 0xFF, 0x62, 0x60, 0x3E, 0x6A, 0x00,
  0xFF, 0x00, 0x62, 0x00, 0x48, 0x00, 0x25, 0x31
};

const byte RSE_PIN = 10;
const unsigned long FRAME_DELAY_US = 1019;  // 573 (frame) + 446 (desired delay)
const unsigned long LOOP_DELAY_US = 9000;

const byte firstByteCycle[] = { 0x20, 0x26, 0x2C, 0x32, 0x38 };
byte cycleIndex = 0;

void setup() {
  pinMode(RSE_PIN, OUTPUT);
  digitalWrite(RSE_PIN, HIGH);          // Always in transmit mode
  Serial1.begin(19200, SERIAL_8O1);     // 8 data bits, Odd parity, 1 stop bit
}

void loop() {
  data[0] = firstByteCycle[cycleIndex];
  cycleIndex = (cycleIndex + 1) % sizeof firstByteCycle;

  for (byte b : data) {
    Serial1.write(b);
    delayMicroseconds(FRAME_DELAY_US);  // Wait until full frame duration 573 µs ~= 11 bits @ 19200 bauds + 446 µs inter-frame gap
  }
  delayMicroseconds(LOOP_DELAY_US);     // Wait before repeating the whole transmission

}

So ask AI why it doesn't work as expected.

Why use Natural Intelligence to find the bugs in AI solutions? Better start using your own intelligence before asking others to provide better thoughts.

7 Likes

Why?
Sorry but, can I suggest you start from the beginning and write your own?

Can you please tell us your electronics, programming, arduino, hardware experience?

Thanks.. Tom.... :smiley: :+1: :coffee: :australia:

1 Like

How accurate is DELAY function?

delay() uses the microseconds() call internally, so it should be accurate to within a a few tens of microseconds at most. (millis() is less accurate, so I guess "blink without delay" is also less accurate.)

An Arduino Uno class chip is not "blindingly fast" - delays on the order of a couple milliseconds can occur just due to the execution time of code.

Yes.

unsigned long dif, ave, pause = 1000, measuredInterval = 100;
int count, high, low = 999;

void setup() {
  Serial.begin(115200);
  Serial.print("waiting ");
  Serial.print(pause);
  Serial.print("ms...");
  delay(2000);
  Serial.println(" starting...");
}

void loop() {
  count++;
  unsigned long start, stop;

  start = micros();
  delay(measuredInterval);
  stop = micros();

  Serial.print("stop ");
  Serial.print(stop);
  Serial.print("us start ");
  Serial.print(start);
  Serial.print("us | difference ");

  dif = stop - start - measuredInterval * 1000UL;
  if (dif < low) low = dif;
  if (dif > high) high = dif;
  ave += dif;

  pad(dif);
  Serial.print("us high ");
  pad(high);
  Serial.print("us low ");
  pad(low);
  Serial.print("us average ");
  pad(ave / count);
  Serial.print("us | count ");
  Serial.println(count);
  delay(pause);
}

void pad(int val) {
  if (val < 10) Serial.print(" ");
  Serial.print(val);
}

Do not use delay(), write in Assembly language and use clock cycles.

Well before resorting to that OP can start with delayMicroseconds() where the job has been done.

If better precision is needed then it’s time to start looking into what’s going on with interrupts or get a faster board….

Hi,

What is the overall scope of your project?
What are you trying to achieve?

Thanks.. Tom.... :smiley: :+1: :coffee: :australia:

1 Like

I think this is quite eye opening. I should leave timings alone and try different approach first. If serial data transfer accuracy were something important, it would be highlighted in the device's specs. Thank you all for advice. Most of it gave me more knowledge.

@TomGeorge
Overall I am looking to make a man in the middle device, which spoofs some data. My primal idea was to fix device's warnings by fixing timings. Then I found a rolling code in the first byte, but that didn't work either. Making "Loop Delay" shorter made it work... odd. Tomorrow I will try "only" forwarding the data by using 1 receiver and 1 sender/forwarder. So far I was just sending a single, captured frame on loop but it did not go anywhere. I have 0 (none) experience in coding. Starting from nothing by myself would be unproportionally time consuming for programming a single device with a single task.

@DrDiettrich
I asked AI but it couldn't explain itself or it did not understand my questions. I am not asking about better thoughts of others. Merely searching for data of Arduino DUE capabilities on serial timings or software ways to make more precise code. There's no LOOP_DELAY_MS anywhere on the web and yet it somehow works. Stuff like that makes learning about code made by AI difficult.

LOOP_DELAY_MS is a constant in your code

it's just a name for a constant, you could have called that foobar_ms, it does not matter.

when you do

it's the same as if you had changed the name and done

  delay(foobar_ms);  

your code will block for 9ms and then continue.

The constant name is OK as it's descriptive. It's the delay at the end of the loop, expressed in milliseconds... (So it's much better than foobar_ms :wink: ). Writing in capital is also a rule commonly used for constants.

Start with more practical goals, not with what you or AI believe right and important.