Search code examples
windowsmatlabreal-timefrequencysampling

Real Time Workaround using windows for fixed sampling time


I am trying to collect data off an accelerometer sensor. I have an Arduino doing the analog to digital conversion of the signal and sending it through a serial port to MATLAB on Windows.

I send a reading every 5ms from the Arduino through the serial port. I am saving that data using MATLAB's serial read in a vector as well as the time at which it was read using the clock method.

If I was to plot the column of the vector where I have saved at which second I read, I get a curve (non-linear), and when I look at the difference between 1 read and another, I see that it is slightly varying.

Is there any way to get the data saved in real time with fixed sampling time?

Note: I am using 250000 baud rate.

Matlab Code:

    %%%%% Initialisation %%%%%
clear all
clc
format shortg

cnt = 1;%File name changer
sw = 1;%switch: 0 we add to current vector and 1 to start new vector
%%%%% Initialisation %%%%%

%%%%% Communication %%%%%
arduino=serial('COM7','BaudRate',250000);
fopen(arduino);
%%%%% Communication %%%%%

%%%%% Reading from Serial and Writing to .mat file%%%%%
while true,
    if sw == 0,
        if (length(Vib(:,1))==1000),% XXXX Samples in XX minutes
            filename = sprintf('C:/Directory/%d_VibrationReading.mat',cnt);
            save (filename,'Vib');
            clear Vib
            cnt= cnt+1;
            sw = 1;
        end
    end
    scan = fscanf(arduino,'%f');
    if isfloat(scan) && length(scan(:,1))==6,% Change length for validation
        vib = scan';
        if sw == 1,
            Vib = [vib clock];
            sw = 0;
        else
            Vib = [Vib;vib clock];
        end
    end
end
%%%%% Reading from Serial and Writing to .mat file%%%%%

% Close Arduino Serial Port
fclose(arduino);

Image 1 shows the data received through serial (each Row corresponding to 1 serial read) Image 2 shows that data saved with the clock

Image 1: enter image description here

Image 2: enter image description here


Solution

  • I know that my answer does not contain a quick and easy solution. Instead it primarily gives advice how to redesign your system. I worked with real-time systems for several years and saw it done wrong too many time. It might be possible to just "fix", but working with your current communication pattern tweaking the performance but I am convinced you will never receive reliable time information.

    I will answer this from a general system design perspective, instead of trying to fix your code. Where I see the problems:

    1. In general, it is a bad idea to append time information on the receiving PC. Whenever the sensor is capable and has a clock, append the time information on the sensor system itself. This allows for an accurate relative timing between the measurements. Some clock adjustment might be necessary when the clock on the sensor is not set properly, but that is just a constant offset.
    2. Switch from ASCII-encoded data to binary data. With your sample rate and baut rate set, you only have 50 bytes for each message.
    3. Write a robust receiver. Just dropping messages you "don't understand" is not a good idea. Whenever the buffer is full, you might receive multiple messages unless you use a proper terminator.
    4. Use preallocation. You know how large the batches you want to write are.

    A simple solution for a message:

    2 bytes - clock milliseconds
    4 bytes - unix timestamp of measurement
    For each sensor
        2 bytes int32 sensor data
    2 bytes - Terminator, constant value. Use a value which is outside the range for all previous integers, e.g. intmax
    

    This message format should theoretically allow you to use 21 sensors. Now to the receiving part:

    To get a first version running with a good performance, call fread (serial) with large batches of data (size parameter) and dump all readings into a large cell array. Something like:

    C=cell(1000,1)
    %seek until you hit a terminator
    while not(terminator==fread(arduino,1));
    for ix=1:numel(C)
        C{ix}=fread(arduino,'int16',1000)
    end
    fclose(arduino);
    

    Once you read the data append it to a single vector: C=[C{:}]; and try to parse it in post-processing. If you manage the performance you may later return to on-the-fly processing, but I recommend to start this way to get the system established.