Serial sample frequency analysis

Hi everyone,

I am currently doing some tests on hardware. I am using a teensy 3.2 to send a byte stream of 87 bytes every 3.125ms (corresponding to 320SPS). The code can be found below.
Arduino code:

long Loop_start;
byte a = 0;
byte byteArray[87];
int som=0;
float Loop_time = 3125;
void setup() {
  Serial.begin(500000);
  byteArray[0] = 0x02;
  for(int i=1; i<85;i++){
    byteArray[i] = a;
    som += a;
  }
  byteArray[85] = som ^ 0xff;
  byteArray[86] = 0x03;
}

void loop() {
  Loop_start = micros();
  Serial.write(byteArray,87);
  while (micros() - Loop_start <= Loop_time) {}
}

Also, data is being received on Processing (code also attached).

Processing code:

import processing.serial.*;
import java.nio.ByteBuffer;

Serial myPort;  // The serial port

static final int PORT = 1, BAUDS = 500000;
static final int FLOATS = 21, BYTES = FLOATS * 4; // alt. Float.BYTES

float[] floats = new float[FLOATS];
byte[] bytes = new byte[BYTES / FLOATS];
byte[] incomingByteArray = new byte[BYTES + 3];

byte soma = 0;
byte checksum = 0;
byte count = 0;
long countReadings = 0;
long previousTime = 0;
long previousCounts = 0;
long countPass = 0;
long previousCountPass = 0;
void setup() {
  // Open the port you are using at the rate you want:
  myPort = new Serial(this, "COM47", BAUDS);
  myPort.clear();  // Throw out the first reading, in case we started reading in the middle of a string from the sender.

  frameRate(1000);
 }

void draw() {
  serialEvent();
}

void serialEvent() {
  incomingByteArray = myPort.readBytesUntil(0x03);
  if ((incomingByteArray != null) && (incomingByteArray.length == BYTES + 3) && verifyChecksum() && incomingByteArray[0] == 0x02) {

    countReadings++;
    
  }
  countPass++;
  long elapsed = millis()-previousTime;
  if (elapsed >= 1000) {
    println("COUNTS = " + (countReadings - previousCounts) + " in " + (elapsed/1000f) + "s");
    previousTime = millis();
    previousCounts = countReadings;
    println("--------------\nFRAME RATE = " + frameCount/ (millis()/1000f)+"\n--------------" );
    println("TIMES PASS ON SERIAL EVENT = " + (countPass - previousCountPass)+ " in " + (elapsed/1000f) + "s\n-------------\n" );
    previousCountPass = countPass;
  }
}

boolean verifyChecksum() {
  for (byte j = 1; j < incomingByteArray.length - 2; j++) {
    soma = byte (soma + incomingByteArray[j]);
  }
  checksum = byte(soma ^ 0xFF);
  soma = 0;
  return checksum == incomingByteArray[incomingByteArray.length-2];
}

The problem:
I set the frameRate to 1000. I know that depending on the machine this frameRate can vary and on my laptop, it is around 835 FPS.
I am counting the number of samples received every 1s (COUNTS), along with the times passed on the serial event and the frame rate. Since printing takes some time from the processor, I am only printing the info every 1s.
However, the number of readings (COUNTS) varies a lot around 320. I also tested sending at 80SPS and the data is being received at 80SPS on Processing

Why is that? If my frame rate is higher than the serial rate update, shouldn’t I be able to capture at 320SPS as well? I am curious about the reason why higher frequencies vary on the flow being received. Does it have to do something with the internal buffer or serial flow?

Thanks in advance.
Best regards

2 Likes

Hi @MiguelSanches, I don’t know the answers to your questions but am interested in the test and surprised how fast things will go without completely falling apart. I wonder how often the checksum fails, which would indicate overrun/buffer-overflow/other error?

Hi @RichardDL

I modified the code to print the number of checksum erros caught:


I am wondering if it has something to do with the serial communication itself or the serial event

In your results there’s one of ‘COUNTS = 307’. That’s a loss of 23 and if there are no checksum errors it’s not losing anything in the comms. Is it possible that when the PC can’t keep up, the comms backs up and slows the program in the teensy? At the end of the teensy code does it arrive at the line with micros() and the time for the next loop to start is already gone? You could test for that and flash a LED.

Hi @RichardDL,

I tried to measure the time elapsed between the start of the loop until the byte array has been sent (TPBA = “Time Per Byte Array” and the loop time circle as well (TPLC _ “time Per Loop Cycle”) using the following arduino code. Take into account that the println also consumes time. But despite some oscillations, teensy is handling everything as far as I tested.

long Loop_start;
byte a = 7;
byte byteArray[87];
int som=0;
float Loop_time = 3125;
void setup() {
  Serial.begin(500000);
  byteArray[0] = 0x02;
  for(int i=1; i<85;i++){
    byteArray[i] = a;
    som += a;
  }
  byteArray[85] = som ^ 0xff;
  byteArray[86] = 0x03;
}

void loop() {
  Loop_start = micros();
  Serial.write(byteArray,87);
  Serial.println();
  Serial.print("TPBA = ");Serial.println((micros() - Loop_start));
  while (micros() - Loop_start < Loop_time) {}
  Serial.print("TPLC =");Serial.println((micros() - Loop_start));
}

Again, that looks perfect, and no packet corruption, so your original question is unanswered. If you run for longer do the numbers average out and there’s no loss? (Change the reporting time from 1 sec to 20 sec?) If there’s no loss, just small variation in rate, then the buffering is doing it’s job preventing loss when when the PC has to do other things.

Hi @RichardDL

I tested today but with a reporting time of 10s.

There are no losses and the average is around 320SPS with small variations. So I guess the problem is due to PC tasks, right?
Also, is there a way to prioritize this specific task? so that if I report every second, the fluctuations on the sample rate will be lower.

Thanks in advance

Assuming you are using Windows, get ProcExp. When ProcExp is running right click on a process and ‘Set Priority’. You could get ProcMon as well. If you start monitoring with that you will be shocked how much your PC is doing while you think it should be idle.

1 Like

Thanks @RichardDL
I will test it but it seems to be the answer.

Best regards