Synchronous Data Transfer between Arduino and Matlab Serially - matlab

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.

Related

Reading Data from Serial Port and Plotting in Real Time

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

How to make sound signal length the same in MATLAB?

I found this speech recognition code that I downloaded from a blog. It works fine, it asks to record sounds to create a dataset and then you have to call a function to train the system using neural networks.
I want to use this code to train using my dataset of 20 words that I want to recognise.
Problem:
I have a dataset of 800 files for twenty words i.e. 40 recordings from different people for each word. I used Windows sound recorder to collect the files.
The problem is that in the code is that the size of the input file is set to ALWAYS be 8000, my dataset on the other hand is not constant, some files are 2 seconds long, some are 3 that means there'll be different number of samples in each file.
If the samples per input signal variate it'll probably generate errors.
I want to use my files to train the system.
How do I do that?
Code:
clc;clear all;
load('voicetrainfinal.mat');
Fs=8000;
for l=1:20
clear y1 y2 y3;
display('record voice');
pause();
x=wavrecord(Fs,Fs); % wavrecord(n,Fs) records n samples at a sampling rate of Fs
maxval = max(x);
if maxval<0.04
display('Threshold value is too large!');
end
t=0.04;
j=1;
for i=1:8000
if(abs(x(i))>t)
y1(j)=x(i);
j=j+1;
end
end
y2=y1/(max(abs(y1)));
y3=[y2,zeros(1,3120-length(y2))];
y=filter([1 -0.9],1,y3');%high pass filter to boost the high frequency components
%%frame blocking
blocklen=240;%30ms block
overlap=80;
block(1,:)=y(1:240);
for i=1:18
block(i+1,:)=y(i*160:(i*160+blocklen-1));
end
w=hamming(blocklen);
for i=1:19
a=xcorr((block(i,:).*w'),12);%finding auto correlation from lag -12 to 12
for j=1:12
auto(j,:)=fliplr(a(j+1:j+12));%forming autocorrelation matrix from lag 0 to 11
end
z=fliplr(a(1:12));%forming a column matrix of autocorrelations for lags 1 to 12
alpha=pinv(auto)*z';
lpc(:,i)=alpha;
end
wavplay(x,Fs);
X1=reshape(lpc,1,228);
a1=sigmoid(Theta1*[1;X1']);
h=sigmoid(Theta2*[1;a1]);
m=max(h);
p1=find(h==m);
if(p1==10)
P=0
else
P=p1
end
end
In your code you have:
Fs=8000;
wavrecord(n,Fs) % records n samples at a sampling rate Fs
for i=1:8000
if(abs(x(i))>t)
y1(j)=x(i);
j=j+1;
end
end
It seems that instead of recording you are going to import your sound file (here for a .wave file):
[y, Fs] = wavread(filename);
Instead of hardcoding the 8000value you can read the length of your file:
n = length(y);
and then just use that n variable in the for loop:
for i=1:n
if(abs(x(i))>t)
y1(j)=x(i);
j=j+1;
end
end
The rest of the code seems to be independent of that 8000 value.
If you are worried that having non-constant file length. Compute n_max, the maximum length of all the audio recordings you have. And for recording shorter than n_max samples pad them with zeros so as to make them all n_max long.
n_max = 0;
for file = ["file1" "file2" ... "filen"]
[y, Fs] = wavread(filename);
n_max = max(n_max,length(y));
end
Then each time you process a sound vector you can pad it with 0 (harmless for you, because 0 means no sound) like so:
y = [y, zeros(1, n_max - length(y))];
n=noOfFiles
for k=1:n
M(k,1:length(filedata{k})) = filedata{k}
end
:P

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 can I output a composite signal made of two signals with different time period at the same time on one audio channel in matlab code?

%-------------------------------------------------------------------
% Function to Generate ECG of 1 heart beat signal
function [Heartbeat,t1] = ECG_Gen (HR,pulse_width,Amp)
Fs = 48000;
delay = ((60/HR)/2)-(0.5*pulse_width);
t1 = -delay:(1/Fs):delay;
Heartbeat = Amp*tripuls (t1,pulse_width);
%-------------------------------------------------------------------
%Test Circuit configuration
function [FECG_MECG,Mixed_ECG,fastTime] = Test_Circuit (FHR,MHR)
Fs = 48000;
%FHR = 150;
%MHR = 60;
Fpulse_width = 30e-3;
Mpulse_width = 60e-3;
FAmp = 0.2;
MAmp = 0.5;
% Fetal ECG Gen
%------------------------------------------------
[FECG,FHR_Delay]= ECG_Gen (FHR,Fpulse_width,FAmp);
% Maternal ECG Gen
%------------------------------------------------
[MECG,MHR_Delay]= ECG_Gen (MHR,Mpulse_width,MAmp);
% Composite signal implementation
%------------------------------------------------
% Set parameters for Composite signal Algorithms
if length (MECG) > length (FECG) % Check for time sequences for both ECG signal
slowECG = FECG; % Set interpolation to slower rate
fastECG = MECG;
timeSeg = length(MECG);
fastTime = MHR_Delay; % Set sampling times
slowTime = FHR_Delay;
else
slowECG = MECG;
fastECG = FECG;
timeSeg = length(FECG);
fastTime = FHR_Delay;
slowTime = MHR_Delay;
end
FECG_MECG = zeros (timeSeg,2); % To hold stereo output
FECG_MECG(:,2) = fastECG(1,:); % Assign higher rate signal to one channel
% Interpolation on the slower rater sampled ECG
slowECGInterp = interp1 (slowTime,slowECG,fastTime);
slowECG = num2cell(slowECGInterp); % Conversion to cell Array in order to remove NaN
slowECG(cellfun(#(slowECG) any(isnan(slowECG)),slowECG)) = [];
slowECG = cell2mat(slowECG);
j = 1;
for i = 1:timeSeg
FECG_MECG(i,1) = slowECG(1,j);
if j == length(slowECG)
j = 0;
end
j = j+1;
end
Mixed_ECG = FECG_MECG(:,1) + FECG_MECG(:,2); % to hold mono output
%while (1)
%sound(Mixed_ECG ,Fs);
%end
%-------------------------------------------------------------------
% Test Wave script
clear all
%clc
clc
Fs = 48000;
%for i = 1:3
%toc
MHR = 60;
FHR = 200;
% Obtain ECG interpolated signal and composite
[FECG_MECG,Mixed_ECG,fastTime] = Test_Circuit (FHR,MHR);
% for test purposes
[MECG,MHR_Delay]= ECG_Gen (60,60e-3,0.5);
%t = timer ('TimerFcn','stat=false','Period',2.0);
wavwrite (FECG_MECG(:,2),Fs,'ECGwav.wav');
i = 0;
a = 1;
tic
while (1)
while (toc < 20*a)
sound (MECG,Fs);
end
toc
a = a+1;
[MECG,MHR_Delay]= ECG_Gen (60*a,60e-3,0.5);
if a > 4
break
end
end
%start(t)
%tic
%t = cputime;
%y = wavread('ECGwav.wav');
%while (1)
% sound(y,Fs);
%end
%toc
Hey Thank you very much for getting back to me, I have made use of your interpolation but still have minor problems from the reading obtained from the Monitor. Fist of all, say I have a constant signal with fixed time period say, 0.8s and I want to add composite signal of say 0.3s, I have managed to use your interpolation method to sample the 0.3s signal at the rate of my 0.8s signal. I think I have solved this issue. Second issue deals with how to combine the two signals which I have managed to do somehow but when I use a while loop in order to repeat the composite signal say over 20s, the signals obtained from the sound output isn't quite what I expected since its sounding the array of stored composite signal which contain ( signal with 0.8s = slowInterp signal of 0.3s ). I have include all the codes and functions. Basically, I need the sound output in while loop to sync with the composite signal for example: if I have a signal that repeats every 1s, I would expect to hear a beep in a while loop that runs for 10s to produce 10 beeps, etc
It is hard to tell exactly what you need, but probably doing interpolation on the sequence that is sampled at the slower rate would work for your application.
If t1s are your times from your faster sequence, and t2s are times from your slower sequence, and slow is your slower sequence then do:
slowInterp = interp1(t2s, slow, t1s);
now you will have the sequency slow sampled at the faster rate.
This is only useful for displaying the time series. If you are doing any spectral analysis, this will introduce artifacts, but that is a more advanced topic.
Using the resample function in the signal processing toolbox could also be useful.

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