# Maximum sampling frequency of ANALOG INPUT through Arduino UNO

171 views (last 30 days)
Valentin Crainic on 3 Mar 2017
Commented: Lakshya Chaplot on 27 Oct 2023
Hi,
I am wondering what is the maximum frequency at which Matlab can sample analog inputs through an Arduino Uno board. The current code that I have been using is below.
. . .
clock = tic;
for j = 1 : (1000)
voltage (j) = readVoltage(a,'A0'); %analog in pin 'A0'
end % for j
b = toc (clock);
disp 'time: '
disp (b)
. . .
Using this code, it only samples at ~100Hz. Is there any way go get it to sample faster, as Arduino states that the board should be able to sample at 10 000Hz (https://www.arduino.cc/en/Reference/AnalogRead)
Thanks in advance!!
##### 0 CommentsShow -2 older commentsHide -2 older comments

Sign in to comment.

### Answers (4)

Brian Rasnow on 19 Oct 2022
Edited: Brian Rasnow on 27 Oct 2022
The Arduino package has enormous overhead, both in Matlab, and on the Arduino. I program the Arduino in C using its IDE to respond to simple commands, e.g.,
#define NUMSAMP 400
const int analogOutputPin = 5;
int data[NUMSAMP + 1][2], i;
unsigned long t0, t1;
void measureWaveformsBinary()
{ // return a buffer of data + sample time in microseconds
t0 = micros(); // time it to compute sample rate
for (i = 0; i < NUMSAMP; i++)
{
data[i][0] = analogRead(A0);
data[i][1] = analogRead(A1);
}
t1 = micros();
data[i][0] = t1 - t0; // put dt at end of data
data[i][1] = (t1 - t0) >> 16; // most significant 2 bytes
Serial.write((uint8_t*)data, (NUMSAMP + 1) * 2 * sizeof(int));
} // measureWaveformsBinary
void setup()
{
Serial.begin(115200);
} // setup
void loop()
{
if (Serial.available())
{
switch (Serial.read())
{
case 'p': // set pwm value
{
int pwmVal = Serial.parseInt();
analogWrite(analogOutPin, constrain(pwmVal, 0, 255));
break;
}
case 'b': // readAndWriteWaveformsBinary
{
measureWaveformsBinary();
break;
}
} // switch
}
delay(1);
} // loop
Upload that code to the Arduino in the IDE, then quit the IDE or disconnect it from the Arduino (set menu Tools -> Port -> to anything else), since serial devices can't be shared between apps. Now open a serial port to the Arduino. I haven't found a great way to identify the Arduino in the port list, but on my Macintosh, it's always the last in the list so, in Matlab:
ports = serialportlist;
ard = serialport(ports{end},115200)
ard.Timeout = 1;
clear ports;
pause(1); % time to boot
To read and plot the data, send the 'b' character to the Arduino and then read the buffer:
write(ard,'b','char');
bin = read(ard,802,'int16');
dt = bitshift(bin(802),16)+bin(801); % microseconds
data = reshape(bin(1:800),2,400)';
t = linspace(0,dt/1000,400)'; % calibrate the time axis
plot(t, data, '.-')
xlabel('msec'); ylabel('ADU')
legend('A0','A1');
Data is read at ~8.9kHz, each channel half of that and there is channel skew (which spline or fft can fix). This loops around 3 times per second, with plot being the bottleneck. h = plot(...) the first time, and changing the line data in the graphics objects speeds that up significantly. Want faster? I could get >20 frames per second with a Raspberry pi Pico or Teensy microcontroller (and 12 bits ...).
Added a uicontrol radiobutton to a figure to toggle an oscilloscope display, and you can nicely see audio waveforms and the like, using just Matlab and a \$5 microcontroller.
##### 3 CommentsShow 1 older commentHide 1 older comment
Brian Rasnow on 27 Oct 2022
Edited: Brian Rasnow on 27 Oct 2022
Sorry, in compacting the code here I deleted its definition --
const int analogOutputPin = 5;
I use pin D5 for PWM output. The Arduino responds to 2 commands. write(ard, 'b','char); returns a block 400 analog time series on A0 and A1, appended with the elapsed microseconds measured on the arduino. The other command, write(ard, 'p 128', 'char); tells Arduino to analogwrite(D5,128); , i.e., set pin D5 to analog voltage ~ 2.4V.
Lakshya Chaplot on 27 Oct 2023
Extremely helpful answer! However, I'm unable to adapt this code to be used on an ESP32 board. Could anyone guide me how to use this logic on an ESP32?

Sign in to comment.

Asher on 12 Jan 2023
Edited: Asher on 12 Jan 2023
I've spent a few weeks (very disjointedly) on this problem(and now have been told that it won't be required, which is quite annoying) so I thought I would share what I've come up with.
as mentioned, using the analog read function in matlab is too time costly to actually achieve a high frequency sampling rate. similarly the UNOs faux usb serial connection severely limits transfer speeds. the best option I've found is using an arduino DUE, with its native USB and ability to use the SerialUSB command.
I am a complete novice in matlab/arduino but this setup has proved reliable, simple and repeatable. It does not require heavy coding knowledge or modification of the arduinos programming. it can also be easily modified to work on something the like the seeeduino xiao, which is even cheaper than an UNO.
Code was tested on both simple things like a potentiometer and a more advanced sensor that I built and it is capable of recording at ~18khz at a 12bit ADC resolution. given the power of the ARM chip the code actually uses a blocking delay to slow it down to ~1khz.
I'm guessing people will take umbridge with the casual nature of my commenting, but it was for me and should be easy enough to understand.
this is the code for loading onto the DUE.
// Simple Serial transfer at high-ish bit rate through native USB Port
unsigned long startMillis;
unsigned long currentMillis;
void setup() {
SerialUSB.begin(115200); // initialize serial communication at an irrelevent but necessary for compiling rate of bits per second
while(!SerialUSB); // allows hardware reset to trigger restart of timer
analogReadResolution(12); // set adc to 12 bit
startMillis = millis(); // initialize zero timer point
}
//Loop continuously feeds data over native serial port, sampling time determined by matlab (haven't decided on optimal time though)
//main but simple code goes here!
void loop() {
int sensorValue = analogRead(A0); // read the input on analog pin 0:
currentMillis = millis() - startMillis; // delete unnecessary counting
String sp = " "; // space delimiter ***** set this to whatever you'd like
String stringtoprint = currentMillis + sp + sensorValue; // create a string containing the time of recording and the adc value
SerialUSB.println(stringtoprint); //print to serial over native SUB
delay(1); // everyone hates a blocking delay, but if this is the only thing you're doing its simple and effective
}
and this is the script i wrote in matlab (I'm on R2022B)
% low overhead serial reader/logger for high-ish speed DAQ from arduino DUE
%optimised to 1khz sampling frequency currently, can run at 18khz.
%Prepare the workspace
clear
% - Initialize serial com with due over native USB - baud rate isn't
% necessary/functional but has to be included
s = serialport("/dev/cu.usbmodem1101",2000000);
% - Open/create text file to store serial data -
% - 'a' setting will create new text file, call it whatever you want
fid = fopen("seriallog.txt",'a');
% - Print to file, printing to command window hidden -
tic
while(toc<3)
data = readline(s);
fprintf(fid,data);
end
%print the elapsed time, it's pointless but helpful.
toc
%add an empty row, i'm guesssing this will be helpful with data
%manipulation as a delimiter, dont do this if you dont want.
fprintf(fid,'%s\n\n',"");
disp("Relax! :D")
##### 0 CommentsShow -2 older commentsHide -2 older comments

Sign in to comment.

Sam McDonald on 6 Mar 2017
Although the maximum reading rate for the analog pin is 10 kHz, this speed cannot be guaranteed with serial communication via Arduino and MATLAB. In other words, there is overhead involved by calling the "readVoltage" function in MATLAB, collecting the signal value from the Arduino pin, and sending it back to MATLAB. It is difficult to say what the theoretical maximum frequency is for data collection, other than performing tests similar to what you have done here.
You can perform a more in-depth analysis on performance by running the profiling tool:
That may help you gain more insight into how much time each function takes to execute, such as the "readVoltage" function.
##### 1 CommentShow -1 older commentsHide -1 older comments
Valentin Crainic on 7 Mar 2017
Thank you! I will give this a try!

Sign in to comment.

Alberto Mora on 29 Dec 2018
Edited: Alberto Mora on 17 Dec 2020
Dear Valentin, I notice the same problem, and I'm also looking for a solution.
At the moment I have adopted the follow approach with discrete results:
1. analogRead in Arduino environment
2. Matlab in a while loop, read the data of serial port coming from Arduino
3. Arduino print the analog value on the serial port, like:
Serial.print( millis() ); Serial.println( signal1_column )
In this last case, the speed is limited to the serial bus. In this way I reached a sampling of about 1-2kHz (even 3kHz but not very stable).
Note: a nice tip to speed up the serial bus is to increase the BaudRate, probably 9600 is too small! Anyhow you will not reach higher frequency of few kHz.
Anyhow I am not sure that is the best approach.
Regards,
A
##### 13 CommentsShow 11 older commentsHide 11 older comments
Alberto Mora on 2 Feb 2021
Edited: Alberto Mora on 2 Feb 2021
If you want to send data using serial port (not fastest & efficient solution, as already mentioned), the code is simply:
Serial.println(analogRead(A0));
Walter Roberson on 2 Feb 2021
I mentioned about USB. If you are connecting the arduino to the matlab host using USB, then it is impossible to get more than 1000 transactions per second, as that is the USB 2 limit and arduino do not have USB 3. Each transaction can be a full buffer of up to 1020 (iirc) bytes with USB 2.1, slightly different for other USB versions. That is bytes, not samples. Each sample will typically need multiple bytes. If you are using print or println then the data is formatted as text, which can take roughly 4 times as much space as the number of bytes per sample. You also need delimiters between the samples if you are using text; println uses newline delimiter. println is often not as efficient to process as packing multiple text samples per line before a newline. Sending text is never as efficient as sending binary instead.
If you are using USB then the baud rate is mostly ignored.
It is also possible to connect arduino to matlab host using a true rs232 serial port. If that is done then the serial port speed does matter, and the latency is lower and you can get higher transactions per second. Noise can be more of a problem though: USB is more immune to noise because of the hardware specifications and ways it transmits.

Sign in to comment.

### Categories

Find more on Analog Filters in Help Center and File Exchange

### Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!