2
votes

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.

1
In MATLAB you use a constant frequency Fs=1000; but it looks like your Arduino is instead working as fast as possible. Both have to use the same sample frequency. - Daniel

1 Answers

2
votes

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.