Reading Data from Serial Port and Plotting in Real Time - matlab

I am looking to take in data from a temperature sensor on an Arduino Uno board and have matlab store the data while also plotting the data on a graph in real time to monitor sensor changes. This will eventually be implemented with a different sensor and this more of a proof of concept. I have two issues currently troubling me:
1) I need to be gathering the data points 1000x a second (which my current code cannot do)
2) After several seconds of monitoring the data a great deal of noise enters the system
Here is my matlab code
SerialPort = 'com6';
s = serial(SerialPort, 'BaudRate', 250000, 'DataBits', 8);
fopen(s);
voltage = 0;
t = 0;
y = 1;
voltage = fscanf(s);
VoltageValue(y,1)=str2double(voltage);
h = animatedline(t,VoltageValue(y,1));
xlim([0 1000]);
ylim([100 200]);
tic
while t <= 1000
voltage = fscanf(s);
VoltageValue(y,1)=str2double(voltage);
addpoints(h, t, VoltageValue(y,1));
t= t+1;
y= y+1;
drawnow
end
toc
fclose(s);
delete(s);
clear s;
Here is my arduino code
int tmppin = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(250000);
pinMode(tmppin, INPUT);
}
void loop() {
// put your main code here, to run repeatedly:
int tempreading = analogRead(tmppin);
Serial.println(tempreading);
}

From what I have tried, the serial communication at 38400bd is enough to transmit an int at 1kHz. You can use 115200bds; your speed of 500000bds is not supported, even if it usually works.
The ADC will have no problem with 1000Hz. The Arduino command analogRead works at 10kHz; and if you would access register of atmel MCU directly, you could speed up analogRead to 100kHz.
Your code is slowed down with drawnow. You dont need to refresh at 1kHz; if you refresh at 10Hz, it will be real time for your eye.
In arduino sketch you should put a delay(1) (1ms), so you know the serial buffer wont get full.

also , to speedup the transfer speed, sending the data as binary will help a lot the Serial.println(tempreading); you used sends data as string. meaning it will send 6 characters each time. (5 characters + the CR). sending as binary will need only 2 bytes.
; a 3X speed increment already.
To send data as binary in arduino , use
serial.write(data_to_send>>8); //send most significant byte
serial.write( data_to_send && f); //send least significant byte

Related

Synchronous Data Transfer between Arduino and Matlab Serially

I am working on a project where i read real time current signal on an Arduino using a Split Core CT. I am able to read exact AC Current and replicate it in Arduino Serial Plotter using the code below.
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.println( (double)(analogRead(A5) - analogRead(A0))*0.009765625 );
}
But I have to do further calculations on it like FFT and THD, so I am sending that data to MATLAB over serial communication. Below is my Matlab Script which reads 1000 data samples, stores it in an array and performs calculations and finally plots it.
clc; close all;
if ~isempty(instrfind)
fclose(instrfind);
delete(instrfind);
end
s1=serial('COM5','Baudrate',115200);
fopen(s1);
Fs=1000;
LoS = 100;
T = 1/Fs;
t = (0:LoS-1)*T;
sig = zeros(1,LoS-1);
str='';
sen=0;
for j = 1:LoS
str=fscanf(s1);
sen=str2double(str);
sig(j)=sen;
end
subplot(2,1,1);
axis([0 LoS -4 4]);
plot(t,sig);
xlabel('Counts');
ylabel('Magnitude');
title('Signal');
Y=fft(sig);
P2 = abs(Y/LoS);
P1 = P2(1:LoS/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(LoS/2))/LoS;
subplot(2,1,2);
axis([0 100 0 10]);
plot(f,P1);
title('FFT(Signal)');
xlabel('f (Hz)');
ylabel('|Power(f)|');
fclose(s1);
delete(s1);
clear s1;
The issue is the frequency of actual signal is 60Hz, but my code outputs a peak at 31Hz. I checked the same code on matlab simulated sinusoids, it gives exact results. But on real data its miscalculating. I implemented the same logic on LABView as well the result remains 31Hz. Can anyone pinpoint my mistake? I am really stuck at this.
Thanks for your time.
You should fix the Arduino sample rate to 1000 samples per second.
As Daniel commented, the Arduino is working as fast as possible, instead of sampling at 1KHz (instead of sampling 1000 samples per second).
You can fix your Arduino loop to sample at 1KHz by making sure each iteration takes 1000 micro-seconds.
The loop below reads the time at the beginning and at the end of each iteration.
At the end of each iteration, there is a delay that completes the duration to 1000us.
void loop() {
unsigned long start_time_us, delta_time_us;
//Returns the number of microseconds since the Arduino board began running the current program
//https://www.arduino.cc/reference/en/language/functions/time/micros/
start_time_us = micros();
Serial.println( (double)(analogRead(A5) - analogRead(A0))*0.009765625 );
//Passed time in microseconds.
delta_time_us = micros() - start_time_us;
if (delta_time_us < 1000)
{
//Delay the remaining time - complete to 1000us (keep sample rate at 1000 samples per second).
//https://www.arduino.cc/reference/en/language/functions/time/delaymicroseconds/
delayMicroseconds((unsigned long)1000 - delta_time_us);
}
}
Please note: I could not verify the solution because I don't have an Arduino board (or simulator).
Remark: sending samples as text strings is inefficient, and may saturate the serial port.
I suggest you do the following:
Send binary data (using Serial.write instead of Serial.println).
Instead of converting the sample to double before sending it from Arduino, send the sample in short format (two bytes): Send the value (short)(analogRead(A5) - analogRead(A0)).
In the MATLAB side, read 1000 binary samples: sig = fread(s1, 1000, 'int16');
Perform the conversion to double and scale samples in MATLAB: sig = double(sig) * 0.009765625.
Arduino code:
//Serial.println( (double)(analogRead(A5) - analogRead(A0))*0.009765625 );
short analog_read = (short)(analogRead(A5) - analogRead(A0));
Serial.write((uint8_t*)&analog_read, 2); //Send two bytes in int16 binary format
MATLAB code:
% for j = 1:LoS
% str=fscanf(s1);
% sen=str2double(str);
% sig(j)=sen;
% end
sig = fread(s1, 1000, 'int16'); % Read 1000 analog samples (each sample is int16).
sig = double(sig) * 0.009765625; % Convert 1000 samples to physical values in double format
The above code is more efficient for both Arduino and MATLAB.
Remark: Keep in mind that I didn't test the code - it's just for demonstrating the concept.

Lsqnonlin on Matlab and Hardware-In-The-Loop with Arduino

I'm having some trouble with the function "lsqnonlin" on Matlab. I'm trying to optimize a PID controller inside Arduino by using this function that has the coefficients (Kp,Ki,Kd) of the PID as decisional variables and the goal function is to minimize the least-square difference of the response minus the reference value.
This link explains better what I'm doing: http://it.mathworks.com/help/optim/ug/lsqnonlin-with-a-simulink-model.html.
The difference between that example and my problem is that I want to perform a sort of Hardware-In-The-Loop simulation, replacing the simulation performed by Simulink with the actual system that I designed, on which a sensor interfaced with Arduino provides the response "yout" to Matlab by Serial Communication.
The problem is that the algorithm on Matlab changes the variables (Kp,Ki,Kd) with a really small step (10^-8 or something like that) and so I can't see any change on the actual response. After a few iterations the algorithm stops saying: "lsqnonlin stopped because the size of the current step is less than
the selected value of the step size tolerance".
I can't understand why the steps are so little and why it stops like that. My code is the following.
pid0 = [10 0 0]; %inizialization of PID parameters
options = optimset('LargeScale','off','Display','iter',...
'TolX',0.001,'TolFun',0.001);
pid = lsqnonlin(#trackpid, pid0, [0 0 0], [], options)
Kp = pid(1); Ki = pid(2); Kd = pid(3);
The trackpid function is:
function F = trackpid(pid)
% Serial communication
s = serial('/dev/cu.usbmodemFD131');
set(s,'BaudRate', 9600);
set(s,'DataBits', 8);
set(s,'StopBits', 1);
% Global Variables
numberOfDatas = 700;
Kp = pid(1)
Ki = pid(2)
Kd = pid(3)
i = 1;
fopen(s);
pause(1.7); %i need to wait 1.7s in order to read the values
%Send to Arduino the PID coefficients
fprintf(s,num2str(Kp));
fprintf(s,num2str(Ki));
fprintf(s,num2str(Kd));
while (fscanf(s,'%c') ~= 't') %Wait for Arduino to start the simulation
end
%Scan
while(i <= numberOfDatas)
yout(i) = fscanf(s,'%f');
i = i + 1;
end
fclose(s);
yout
F = yout - 21; %evaluate the difference between the response and the reference
Thank you in advance.

ECG Live Monitoring/Visualization + Data conversion on Matlab

I am working on ECG project for biometric identification: I use Arduino + Olimex ECG/EKG Shield and transmit the data to MatLab.
I am facing two issues:
Monitoring on real time: The X-Axis scale changes automatically and will compress the ECG after long time. I want to visualize a specific view so QRS complexes will be clearly visual. Any generic code?
Getting real voltage values: Analog.read() on Arduino converts voltages from 0-5 to 0..1023 and then we send them via serial communication. On Matlab, ECG does not have specific min and max to get the real values of the voltages to extract the characteristics. Any ideas about how that could be done.
Thank you so much in advance.
Waiting for your replies.
Kind regards,
Badreddine
edit disclaimer, I wrote this code from memory, so it may be buggy, apologies in advance
1.Lets say your data is stored in some array data_array and every time we get a sample we do something like this
data_array = [data_array, sample];
Now let's say in our viewer we only want to view 20 samples. we can do something like this
num_visible_samples = 20; %define this somewhere outside your data aquisition loop
%gets new data somehow
data_array = [data_array, new_data_point];
%plots all data
plot(data_array);
length_data_array = length(data_array);
if (length_data_array >= num_visible_samples)
%this makes the plot window only show the current data point and the
%previous few data points, the Yaxis will automatically be scaled
xlim([length_data_array -num_visible_samples, length_data_array ]);
end
another thing you could do (which will be faster) is to only plot the number of samples you desire
num_visible_samples = 20; %define this somewhere outside your data aquisition loop
%gets new data somehow
data_array = [data_array, new_data_point];
%plots all data
plot(data_array);
length_data_array = length(data_array);
if (length_data_array >= num_visible_samples)
%plots only the specific number of data points
plot(data_array(end-num_visible_samples,end));
%im not sure if you need to specify the axis, but i think you do
xlim([length_data_array -num_visible_samples, length_data_array ]);
else
plot(data_array);
end
2.Converting the values back and forth is pretty simple. This is jsut some psuedocode
maxV = 5.0;
minV = 0.0;
num_of_quantized_values = 1024;
%this says how much the lsb of the adc is, basically we just divide our
%input range (0-5 volts) by our output range (0-1023) => 4.88mV
adc_resolution = (maxV - minV) / num_of_quantized_values;
to convert the digital value back to an analog voltage we multiply our resolution by the value returned
val_in_volts = adc_digital_reading * adc_resolution;
so for example, say the adc read 256
256 * 4.88mV = 1.25
edit2
Since atmega chips usually have 10bit ADCs you get the data adc data in two bytes. The lower 8 and then the upper 2. The fix isn't too bad, say we have two variables
lower_byte is uint8
upper_byte is uint8
so I would do it this way (there are various methods depending on implementation)
adc_digital_reading = bitor(bitshift(uint16(upper_byte ),8), uint16(lower_byte));
%mirrors this c code
%adc_u16 = (upper_byte << 8) | lower_byte;
The important part is converting the uint8 types to uint16, or it wont work properly
to be really safe you could do
adc_digital_reading = bitand(bitor(bitshift(uint16(upper_byte ),8), uint16(lower_byte)),1023);
%adc_u16 = ((upper_byte << 8) | lower_byte) & 0x03FF;

How to process multiple mics input Audio stream in matlab in real time

I need to get 3 input audio stream to matlab simultaneously using 3 USB mics and find out the highest amplitude in real time.
this is the mics initialization
mic1= dsp.AudioRecorder('DeviceName','ÇáãíßÑæÝæä (10- USB PnP Sound Device)', 'SampleRate', 48000, 'NumChannels', 1);
mic2= dsp.AudioRecorder('DeviceName','ÇáãíßÑæÝæä (9- USB PnP Sound Device)', 'SampleRate', 48000, 'NumChannels', 1);
mic3= dsp.AudioRecorder('DeviceName','ÇáãíßÑæÝæä (8- USB PnP Sound Device)', 'SampleRate', 48000, 'NumChannels', 1);
frame1=step(mic1);
frame2=step(mic2);
frame3=step(mic3);
what are the next steps?
By examining your code, you have three audio signals with one channel each. If I understand what you want correctly, you want to find the highest sound made by any one signal overall over time. However, if I understand you correctly, you can't do this in real time as step for the AudioWriter can only capture one frame at a time. Because you're specifically trying to capture from all three audio devices, and the only way for you to capture audio is with step, you'll have to serially call step for each signal that you have.
Therefore, you'll need to capture all three audio signals separately, and then do your analysis. You'll also want to clip the sound after a certain point, so perhaps something like 5 seconds. Therefore, you'd do something like:
time_end = 5;
%// Capture audio signal 1
tic;
frame1 = [];
while toc < time_end
audio_in = step(mic1);
frame1 = [frame1; audio_in(:)];
end
%// Capture audio signal 2
tic;
frame2 = [];
while toc < time_end
audio_in = step(mic2);
frame2 = [frame2; audio_in(:)];
end
%// Capture audio signal 3
tic;
frame3 = [];
while toc < time_end
audio_in = step(mic3);
frame3 = [frame3; audio_in(:)];
end
After this point, because the sounds will probably all be uneven length, you'll want to zero pad all of them so they all match the same length. After this, it's a matter of first finding the maximum amplitude for each sample for all three signals, and then finding the maximum out of all of this.
I'm not quite sure how the signals are shaped... if they are row or column vectors, so let's just make sure they're all column vectors. Then, use max and operate along the columns and find the maximum for each point in time, then find the maximum out of all of these.
Therefore:
%// Find lengths for all three signals
l1 = numel(frame1);
l2 = numel(frame2);
l3 = numel(frame3);
max_length = max([l1, l2, l3]);
%// Zero pad signals to make same length
frame1_pad = zeros(max_length,1);
frame2_pad = zeros(max_length,1);
frame2_pad = zeros(max_length,1);
frame1_pad(1:l1) = frame1;
frame2_pad(1:l2) = frame2;
frame3_pad(1:l3) = frame3;
%// Find maximum among each sample
max_signal = max([frame1_pad, frame2_pad, frame3_pad], [], 1);
%// Find the maximum amplitude overall and location
[max_amplitude, loc] = max(max_signal);
max_amplitude will contain the highest point overall at a particular time point for each of the three signals, and loc will tell you the location in the array of where it was found. If you want to find the actual time it occured, simply take loc and multiply by your sampling time (1/48000). Bear in mind that loc will be 1-indexed instead of 0-indexed, and so you need to subtract by 1 before multiplying by the sampling rate.
Therefore:
time_it_happened = (loc-1)*(1/48000);
time_it_happened will contain that time which the highest amplitude happened.
Good luck!

Setting sampling rate in MATLAB for Arduino

I'm having trouble getting consistent results with the code I am using. I want to run my Arduino for a specific amount of time (say 20 seconds) and collect data from the analog pin with a specific sampling rate (say four samples a second). The code is as follows.
a_pin = 0;
tic;
i = 0;
while toc < 20
i = i + 1;
time(i) = toc;
v(i) = a.analogRead(a_pin);
pause(.25);
end
Is there a way to set the loop to run a specific time and then in the loop sample at a different rate?
You can try this:
a_pin = 0;
fs = 4; % sampling frequency (samplings per second)
mt = 20; % time for measurements
ind = 1;
nind = 1;
last_beep = 0;
tic;
while toc < mt
time(ind) = toc;
v(ind) = a.analogRead(a_pin);
% wait for appropriate time for next measurement
while( nind == ind )
nind = floor(toc*fs) + 1;
end
ind = nind;
% beep every second
if (ceil(toc) > last_beep)
beep(); % don't know if this one exist, check docs
last_beep = ceil(toc);
end
end
Maximal sampling time for a single Arduino analog read command is around 0.04 s, in practice I'd go minimally 0.05. Adding two read operations is in the order of 2*0.04, in practice more like 0.1 s. I think it is mainly limited by the USB communication speeds.
I am also new at arduino, but having implemented a real time analysis for EEG using it, on practice, I was able to sample 2 analog channels with a samplinf frequency between 57 and 108Hz. It was very variable (calculated through tic/toc), but it is still proper for realtime processing in my case.
My code uses a While loop, a series of memory updates, digital pin manipulations, plot of trace (drawnow) and seems to run smoothly enough
My answer is simply here : 0.0283 sec for sampling 2 analog inputs in my case.
Cheers