Maximum sampling frequency of ANALOG INPUT through Arduino UNO
Mostrar comentarios más antiguos
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!!
Respuestas (4)
Brian Rasnow
el 19 de Oct. de 2022
Editada: Brian Rasnow
el 27 de Oct. de 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 comentarios
Louis
el 26 de Oct. de 2022
Hi Brian,
I'm pretty new to combining Arduino and Matlab, so I started by copy-pasting your program on the Arduino IDE to upload it on my UNO R3.
However, the code couldn't compile : Compilation error: 'analogOutPin' was not declared in this scope. Indeed analogOutPin isn't declared anywhere, so I tried to understand what your code was doing and after some time I still can't figure out certain things.
I'm confused when it gets to the switch. I understand that you expect to read a 'b' sent by your Matlab program whenever you want to read your analogPins. However, I don't understand why you would expect to read a 'p', and I don't understand what you are doing in all the corresponding case.
I hope that I was clear enough,
Thanks in advance.
Brian Rasnow
el 27 de Oct. de 2022
Editada: Brian Rasnow
el 27 de Oct. de 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
el 27 de Oct. de 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?
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")
Sam McDonald
el 6 de Mzo. de 2017
0 votos
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 comentario
Valentin Crainic
el 7 de Mzo. de 2017
Alberto Mora
el 29 de Dic. de 2018
Editada: Alberto Mora
el 17 de Dic. de 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:
- analogRead in Arduino environment
- Matlab in a while loop, read the data of serial port coming from Arduino
- 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 comentarios
Emma Hartman
el 1 de Feb. de 2019
Hi Alberto,
I'm a high school student building an EEG machine. I noticed that what you're doing above does exactly what I want to do, but I'm trying to read data from multiple analog inputs to the Arduino (at least 500 samples per second per channel for multiple minutes). Would you be willing to share your code for what you described above and perhaps help me expand it to suit my needs?
Best,
Emma
Alberto Mora
el 2 de Feb. de 2019
Editada: Alberto Mora
el 2 de Feb. de 2019
even me I was a university student of Mechanical Engineering until a few months ago and I'm happy to help a colleague.
In attached there is the code. Take it and improve it according to your needs.
When I have time, I will put the updated version of the code on MATLAB File Exchange.
Anyhow, ask me if it is not clear the code.
Emma Hartman
el 2 de Feb. de 2019
Hi again Alberto,
Thanks so much for the help! I took your code and changed the serial port and baud rate, but whenever I try to run the code unaltered I get several error messages saying for example:
"The measurment point #2 have got a wrong time equal to -3.9e-05,
while the theoretical time should be t(n-1)+dt = 0 + -5.4e-05, for this reason it will be replaced.
The measurment point #3 have got a wrong time equal to -1e-06,
while the theoretical time should be t(n-1)+dt = -5.4e-05 + -5.4e-05, for this reason it will be replaced.
The measurment point #4 have got a wrong time equal to -3.9e-05,
while the theoretical time should be t(n-1)+dt = -0.000108 + -5.4e-05, for this reason it will be replaced."
and I can't record anything afterwards. Could you help me resolve this issue?
Thanks,
Emma
Emma Hartman
el 2 de Feb. de 2019
Sorry-- I solved part of the problem; my serial port was returning data as a column vector instead of the expected row vector. When I transposed the data, the code runs without error. However, my Arduino has five data channels but the initialization vector only has four values and only three plots are displayed. Any ideas of what I can do to fix this?
Thanks again,
Emma
Alberto Mora
el 3 de Feb. de 2019
Editada: Alberto Mora
el 17 de Dic. de 2020
Dear Emma,
here is a light version of the code. Anyhow, consider to modify the code your own according to your needs. Maybe the MATLAB "debugger mode" can help you to understand better the problems.
Please use a higher boud rate, to increase the speed of the serial port.
Regards,
Alberto
Govind Chauhan
el 17 de Dic. de 2020
Hello Alberto,
I am facing a problem to increase the sampling frequency in matlab itself
How can i do that..?
Alberto Mora
el 17 de Dic. de 2020
Hi Govind,
please have a read to this post, and you will have all the knowledge that I discover untill now.
If you need to go faster than few kHz (using the simple serial connection), probably you need to send data via Ethernet cable (with a specific Arduino Shield), or the simplier solution is to save all the data on an SDcard using the SPI bus (very fast!). In this last case, you cannot read the data in real time from the SDcard.
However you can use serial bus for real time display, sending few points (like at 500 Hz), and then save all the data at full speed on an SDcard.
Try and then post here your results in order to share your experience with other users.
Have a nice day.
A
Govind Chauhan
el 18 de Dic. de 2020
Hi,
Thanks,
Actually i am reading aan analog input data (motor current ) attacehd to arduino uno and i want the data stored in matlab in variable along with time.
if serial monitor of arduino ide is used i am geeting 1700 Hz freq which i need nut no timestamps.
when i shift to matlab 30 Hz sampling rate is obtained.
How do i increase that to 1700 Hz..?
PLS help.
Thanks!
Walter Roberson
el 18 de Dic. de 2020
USB up to USB 3.0 is limited to 1000 master/slave cycles per second (USB 3.1 could potentially go higher if I am reading correctly.)
Therefore the only way you could exceed 1 kilo samples per second is if you send more than one sample per packet. Which you cannot do using readVoltage()
To improve your throughput you need to deploy some code on the arduino side that reads and buffers several samples before sending them.
Alberto Mora
el 18 de Dic. de 2020
Editada: Alberto Mora
el 18 de Dic. de 2020
Govind,
As I already write above, the matlab routine is quite slow.
If you need 1700 Hz, send simply the analog results of your AnalogRead() Arduino function via serial port (as single point or as a buffers as suggested by Mr. Roberson).
Please, READ CAREFULLY the entire post hystory, and have a look to the code that I posted in the above post that allows you to do what you are looking for.
Regards, A
Apurv Sharma
el 2 de Feb. de 2021
Can anyone please share the arduino code for above mentioned technique?
Thanks in advance.
Alberto Mora
el 2 de Feb. de 2021
Editada: Alberto Mora
el 2 de Feb. de 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
el 2 de Feb. de 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.
Categorías
Más información sobre MATLAB Support Package for Arduino Hardware en Centro de ayuda y File Exchange.
Productos
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!