Speed limitations in basic data transfer

Very basic question, but I can't reconcile what I've been reading in other posts with what I see in my own experiments.

In an introductory teaching lab, I would like to illustrate the ability of Arduino (Uno) to acquire analog signals, convert them to digital, and send them to a computer. This should also show the limitations (ADC resolution with 10 bits, as well as maximum sampling / transfer frequency). To this aim, I hooked up a signal generator set to a sine wave, 200mV peak to peak, offset by 300mV to be sure that the signal is always positive, and I vary the frequency.

The signal generator is connected to A0 on the Arduino, and I monitor the signal on the Serial Monitor of the Arduino IDE. It works fine: I can see the discrete levels due to the ADC resolution, and for low frequencies I get a sine wave that looks just like the one on the oscilloscope. However, I am puzzled with the fact that after about ~300Hz, the sine wave already shows significant degradation due to poor sampling rate. I would have expected this to happen at a substantially higher frequency.

I changed the baud rate for the Serial connection, assuming this was the problem, but even at the maximum option available (2000000 bauds) input signals above ~300Hz don't seem to be well resolved in time. I wonder what I'm doing wrong, and how I should make sense of the results, and possibly improve the situation (though it's not critical: I don't actually need high transfer rate, but I would like to understand where the limitations come from).

Basic code, slightly edited from the AnalogReadSerial example:

void setup() {
  Serial.begin(2000000);
}

void loop() {
  int sensorValue = analogRead(A0);
  Serial.println(sensorValue);
  delay(1);  // delay in between reads for stability
}

I moved your topic to an appropriate forum category @baptnz.

In the future, please take some time to pick the forum category that best suits the subject of your topic. There is an "About the _____ category" topic at the top of each category that explains its purpose.

This is an important part of responsible forum usage, as explained in the "How to get the best out of this forum" guide. The guide contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

Hello @baptnz ,

What do you think would be unstable without this? How do you think having a delay makes it stable?

What is the maximum possible number of samples you can get when there is a delay of at least 1ms between them?

What is the minimum number of samples you need to take to get a half decent digitisation of a sine wave?

How does all the above relate to your results?

Umm. With a 1 ms delay, plus the actual execution time, you're likely not going to sample much faster than 800, maybe 900 samples per second. For 300 Hz, that's barely above Nyquist.

here is an example using an ESP32 reading ADC every 100uSec and displaying using DAC

// ESP32 DAQ
// ESP32 timer interrupts 10000times/second to read ADC and output to DAC

// from ESP32 Sine Wave Example https://deepbluembedded.com/esp32-dac-audio-arduino-examples/

/*
* LAB Name: ESP32 Sine Wave Generation Example
* Author: Khaled Magdy
* DeepBlueMbedded 2023
* For More Info Visit: www.DeepBlueMbedded.com
*/

#include <driver/dac.h>

// Timer0 Configuration Pointer (Handle)
hw_timer_t *Timer0_Cfg = NULL;

volatile int counter = 0;

// The Timer0 ISR Function (Executes Every Timer0 Interrupt Interval)
void IRAM_ATTR Timer0_ISR() {
  // read ADC output to DAC
  unsigned int adc=analogRead(35)>>4;   // adc/16 - 12bits to 8bits
  dac_output_voltage(DAC_CHANNEL_1, adc);
  counter++;
}

void setup() {
  Serial.begin(115200);
  
  Serial.println("\nESP32 DAC sine wave");
  // Configure Timer0 Interrupt 10000/second
  Timer0_Cfg = timerBegin(0, 800, true);
  timerAttachInterrupt(Timer0_Cfg, &Timer0_ISR, true);
  timerAlarmWrite(Timer0_Cfg, 10, true);
  timerAlarmEnable(Timer0_Cfg);
  // Enable DAC1 Channel's Output
  dac_output_enable(DAC_CHANNEL_1);
  analogSetWidth(12);    // ADC 12bit resolution
  analogSetClockDiv(1);  // fastest converion time
}

void loop() {
  static long timer = millis();
  if (millis() - timer > 1000) {
    Serial.println(counter);
    counter = 0;
    timer = millis();
  }
}

scope displays (blue is from signal generator yellow is DAC output)
image

serial monitor displays samples/second

10006
10006
10006
10005
10005
10005
10006

Good point – I didn't have the code with me so I copied over the AnalogSerial example and kept this line by mistake in my post. I'd commented it out in my experiments, but you're right: I did not think enough of what a decent digitisation would require – I just saw a clear degradation after 300Hz and that seemed strange. After doing more experiments, and checking the actual timestep between measurements, it makes more sense.

1 Like

If you use the default settings on a 5V Arduino, the resolution of the ADC is about 5 mV/step. You can use another ADC reference, such as the internal 1.1 V reference, to get about 1 mV/step.

You need to individually calibrate the internal reference as the accuracy is +/- 10%, but it is very stable. Of course, input signal voltages above the ADC reference are converted to 1023.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.