Question regarding baud rate and sample rate for serial communication

Hello! This is my first post. I wanted to make a question about baud rates and sampling rates.

I'm using Arduino UNO as an analog to digital converter, getting data from a sensor and sending it via serial communication with the USB cable to my computer, and reading it on Python for processing. The sensor is to capture the electromyographic signal from the surface of the skin. The bandwidth of the signal is roughly 5-500 Hz, so according to the Nyquist theorem, my sampling rate should be at least 1 kHz. I want to do this with Arduino UNO.

I've set a simple code in order to get started with my sensor and with programming. With this I've been able to send data from Arduino to Python effectively but now I want to get serious.

// Lectura del valor de un sensor
// Leo el valor de un sensor y lo envío a través del puerto Serie
int sensorPin = A0;  // salida del sensor en pin 0                  
int sensorValue;     // variable para almacenar el dato

void setup()
{
  // inicializo la comunicación serie
  Serial.begin(115200);
}

void loop()
{
  sensorValue = analogRead(sensorPin);  // leo el pin A0   
  Serial.println(sensorValue);         // mando el dato al serial


  delay(100);             // Pauso 100 milisegundos
}

I'm reading the sensor on pin A0. I've put a delay of 100 ms just to test the code, although I've read that "delay" is not the most reccomended function for these type of loops.

My question is... what value of delay (or millis, or micros..) should I use in order to get a signal that is sampled at 1 kHz (or higher, if possible)? Is the 115200 baud rate proper for this type of data sending?

What format is the data that I'm sending? Is it an 8 bit, or a 10 bit type of byte?

Thanks.

Welcome to the forum

Why have a delay() at all if you want to read the data as fast as possible ?

As written the data will be sent as a string of ASCII characters

Which Arduino board are you using ?

I'm using the delay to tell the Arduino how much time to wait before reading the sensor and sending a new value. Therefore, to establish the sampling rate. In my head, a delay of 100 ms means that every second I'm sending 10 readings of the sensor, therefore, a sampling rate of 10 Hz. So for my desired sampling rate of 1 kHz I should use a delay of 1 ms. However I've read now that doing this with delay is not correct, because the Arduino also takes time to read the sensor data and "put it" in the serial port, so I would not be getting exacly 1000 samples each second.

I'm using an Arduino UNO board.

The print command sends characters. sensorValue can be every value between 0 and 1023. So your print command will send 1 to 4 number characters + CR/LF.

That's correct. You have to use the mills() function to get eqal distances for your measurement.

Something like this:

// Lectura del valor de un sensor
// Leo el valor de un sensor y lo envío a través del puerto Serie
int sensorPin = A0;  // salida del sensor en pin 0
int sensorValue;     // variable para almacenar el dato

unsigned long intervalTime = 100;
unsigned long lastMeasureTime;

void setup()
{
    // inicializo la comunicación serie
    Serial.begin(115200);
}

void loop()
{
    if ( millis() - lastMeasureTime > intervalTime ) {
        lastMeasureTime += intervalTime;
        sensorValue = analogRead(sensorPin);  // leo el pin A0
        Serial.println(sensorValue);         // mando el dato al serial
    }

}

Thanks. So is the baud rate of 115200 appropriate for this sampling rate? Or should it be lower or higher?

Definitely not lower. 115200 Baud needs 8.68µs per bit. With ten bits per character ( startbit, 8 databit, stopbit ) you need 86.8µs per character. With a max of 6 characters you need 0.52ms per sample periode. That should work with an interval of 1ms.

But if you want the fastest sampling rate possible

then why introduce a delay ?

It looks like @sebastian1794 wants to sample rapidly and also know the rate at which that can be done.

So some kind of mechanism must be employed to make sampling regular.

How long does it take to get a sample?

How long does it take to transmit a sample?

If it was 10 Hz rate, a loop set to run every 100 ms would be fine.

At 1000 Hz, loop every ms, still possible… 1 ms is an eternity in some contexts, but with sampling and transmitting you may not be able to live within that narrow slice of time.

Code it to run flat out first, just no delay. Measure the rate. Back off from that conservatively to something you can work with or… revisit whether your approach will work.

There are faster processors, but the sampling and commas are a hard limit.

a7

@MicroBahner , @UKHeliBob , alto777
Thanks for the replies.
I don't really want to have the highest sampling rate possible (since I'm using the data for processing, an unnecesary amount of data will make the processing time longer). I used the delay function to work with a fixated, known sample rate.

Based on the math that MicroBahner did in his reply, I see that it's possible to use a sampling rate of, let's say, 1.5 kHz (I want to use a frequency a bit higher than 1 kHz to avoid aliasing in my recorded signal). With that, my sampling period would be 0.66 ms. Let me see if I'm calculating properly:

(1/115200) x 10 bits x 6 characters = 0.52 ms as said by MicroBahner, still fits into a 0.66 ms window for 1.5 kHz sampling.

I'm guessing he used 6 characters because 4 characters is for the data (0000-1023) and the other two for the \n and \r that appear in my serial port? I know that when reading the data on Python, I have to remove those before converting the string to float.

I've just tested the initial code again, and when I see the data on Arduino's serial monitor, it shows the value as a number in the 0-1023 range. When reading the data on Python I use the following code:

    b = ser.readline()         # read a byte string
    string_n = b.decode()  # decode byte string into Unicode  
    string = string_n.rstrip() # remove \n and \r
    flt = float(string)        # convert string to float

On my recent test, the final measured value was 596. So "flt" showed 596.0, "string" showed '596', "string_n" showed '596\r\n' and "b" showed b'596\r\n' .

So how many characters should I consider in the minimum sampling rate calculation?

b is the one that determines the transfer rate, so 6 characters. If you need faster, you can strip the '\r' (modified code from post #4)

void loop()
{
    if ( millis() - lastMeasureTime > intervalTime ) {
        lastMeasureTime += intervalTime;
        sensorValue = analogRead(sensorPin);  // leo el pin A0
        Serial.print(sensorValue);         // mando el dato al serial
        Serial.print("\n");
    }
}

TWO start bits, 11 bit frame time.

1. Let us assume that the Fig-1 depicts the nature of signal you are acquiring and digitizing using ADC of UNO. The assumed sampling rate 1500/sec.
anaSigSample
Figure-1:

2. Theoretically the sampling is 2xmaxFrequency. Practically, it is about 10 times (5000 samples/sec); however, I would recommend to sample the signal five times which means 5x500 = 2500 samples in one second in order to get closer reproduction of the original signal. Then the time gap between two samples is: 400 us. At 1500 samples/sec, the time gap between two samples is: 666 us. In the calculations that follow, I would use 1500 samples/sec.

3. The ADC will take about 100 us time (1/125 kHzx10 = 80 us) to finish the conversion of a sample. There is still 566 us time in hand to carry out Serial transmission of a sample, other tasks (if any), and other house keeping jobs.

4. As you are using UNO, (probably) you have to use Software UART Port (SUART) which works well only at 9600 Bd. Hardware UART Port (which can work at higher Bd) of the UNO may not be available as this port remains engaged with PC/IDE/SM.

5. Every sample of the data is 16-bit wide (only lower 10-bit carries information) whose range is: 0000 - 1023 (0x0000 - 0x03FF).
(1) If sending ASCII codes for the decimal digits, then the time required to transmit one sample would be: 4-characterx1/9600 = 4x10x1/9600 = 4166 us. This is not feasible as the available time is only 566 us. (To transmit one character/digit, a 10-bit wide frame is required, Fig-2).
185-00
Figure-2:

(2) If sending ASCII codes for the hex digits, then the time required to transmit one sample would be: 3-characterx1/9600 = 3x10x1/9600 = 3125 us. This is not feasible as the available time is only 566 us.

(3) If sending raw binary codes (2-byte) of a sample, then the time required to transmit one sample would be: 2-bytex1/9600 = 2x10x1/9600 = 2083 us. This is not feasible as the available time is only 566 us. When sending binary over SUART Port, there is overhead in sending the at least 2-byte (3-byte better) synchronization pattern and 1-byte checksum for every packet of data (a packet size could be max 60-byte).

6. Considering the facts of Step-4, I would suggest to use one of the Hardware UART Ports (UART1 or UART2 or UART3) of MEGA and acquire 2500 samples/sec (or 5000 samples/sec) and transmit ASCII codes of the decimal digits at suitable Bd (Baud Rate).
(1) 1500 samples/sec; sending ASCII codes of decimal digits; UART1 of MEGA at Bd = 115200.
Time required to transmit one sample : 347 us when there is 566 us available

(2) 2500 samples/sec (time gap between two samples: 400 us) ; sending ASCII codes of decimal digits; UART1 of MEGA at Bd = 115200.
Time required to transmit one sample : 347 us (4x10x1/1152000) when there is 400 us available (marginal). Increase Bd to 230400 or 250000.

(3) 5000 samples/sec (time gap between two samples: 200 us) ; sending ASCII codes of decimal digits; UART1 of MEGA at Bd = 250000.
Time required to transmit one sample : 160 us (4x1x1/250000) when there is 200 us available.

7. TC1 (Timer/Counter 1) Module of MEGA could be used to generate 200 us Time Tick to periodically interrupt the MCU to acquire data and send it to destination without any buffering.

8. Example Sketch (testing UART1 communication)
(1) MEGA (Transmitter) Sketch

//MEGA acquire 5000 samples/sec and send at Bd = 250000
volatile bool flag = false;
int counter = 0;

void setup() 
{
  Serial.begin(250000);
  Serial1.begin(250000);
  //--------------------
  TCCR1A = 0x00;  //TC1 is an upcounter in normal mode
  TCCR1B = 0x00;  //TC1 is OFF
  TCNT1 = 65486;  //preset value for 200 us Time Tick
  TCCR1B = 0x03;//Start TC1 at clkTc1 = 16MHz/64
  bitSet(TIMSK1, TOIE1);  //TC1 overflow interrupt is enabled
}

void loop() 
{
   if(flag == true)
   {
    int y = analogRead(A0);
    Serial.println(y, DEC);
    Serial1.print(y, DEC); 
    counter++;
    if(counter == 5000);
    {
      Serial1.write(0x03);  //End-of-packet mark
      Serial.println("5000 samples are sent.");
      counter = 0; 
    }
    flag = false;
   }
}

ISR(TIMER1_OVF_vect)
{
  TCNT1 = 65486;
  flag = true;
}

(2) Receiver (DUE) Sketch

char myBuff[5000];
int i = 0;

void setup()
{
  Serial.begin(250000);
  Serial1.begin(250000);
}

void loop()
{
  byte n = Serial1.available();
  if (n != 0)
  {
    char y = Serial1.read();
    if (y != 0x03)//end of packet found
    {
      myBuff[i];
      i++;
    }
    else
    {
      Serial.println("5000 samples received.");
      Serial.println("Process Data.");
      i = 0;
      while(1);
    }
  }
}

I have never heard of 2 start bits in a UART connection. Maybe you meant 2 stop bits? But that is an ancient frame time for old mechanical teleprinters.
The default setting for Serial is 8N1, which means 8 data bits, no parity and 1 stop bit.

And one "Start Bit" is understood by default.

your right, its two stop. but it still totals out to 11 bit times per char, not ten. Char rate = baud / 11. 9% lower than baud/10.

The default of Serial is only ONE stop bit. Look here
SERIAL_8N1 (the default)

Note that sending variable length messages, such as "9\n" and "1000\n" can systematically skew your sample interval, since the longer strings can take more time to transmit.

To eliminate this source of timing error you could pad the data with spaces or zeros, or add 1000 to offset all the results to be 4-digit numbers.

The println statement writes the characters to the send buffer only . The time difference between writing 2 or 5 characters to the buffer is very small. analogRead takes considerable more time. Transmitting the chars over the line is done by interrupts, and this happens during the delay() in this case. And it doesn't make the delay longer.
But of course, to get an exact sample rate, delay is not the way to go.

If you are operating near where baud-rate and sample rate interact, you could be filling up the buffers.

There was some recent thread that had an interesting plot of a sine wave distorted by I/O-bound sampling. I think it had sharper peaks than the more rounded troughs. (Edit -- it had rounded peaks and sharp troughs: Read in sine signal It's problems were with both aliasing and systematically varying dt)

Yes, that should be considered and must not happen. I think all the baudrate computations above are with this in mind.