1
$\begingroup$

I'm using simulink support package for arduino to read serial data from port2 in Arduino due. My plan is to read signed integers (-415 for example) representing motor speed and feed it to the pid controllers as in the image.enter image description here

from the far end i'm sending delimited data in the following shape . The matlab function in sumlink is supposed to read the received ASCII characters and add them to a variable until it reaches the end character '>'. I'm using the following simple code just to give the output on both the Right and Left to check if I'm receiving the correct data, However I'm not.

    function [Right ,Left] = fcn(data,status)


SON = '<';
EON = '>';

 persistent receivedNumber;
 receivedNumber = 0;

 persistent negative;
 negative = false;


 if(status ==1)

    switch(data)

    case EON
        if (negative)
            receivedNumber = -1*receivedNumber;


        else
            receivedNumber = 1*receivedNumber;
        end


    case SON
        receivedNumber = 0;
        negative = false;


    case {'0','1','2','3','4','5','6','7','8','9'}
        receivedNumber = 10*receivedNumber;
        receivedNumber = receivedNumber + double((data - 48));


    case '-'
        negative = true;  



    end       

 end

    Right = receivedNumber;
    Left = receivedNumber;



end

Can anybody tells if there are any other approaches to read multiple signed digits in simulink? Taking into consideration that I have to use the support package for Arduino since my pid controllers are already configured in Simulink and interfaced with port2 in Arduino (which will be connected to BeagleBone black)

$\endgroup$

1 Answer 1

3
$\begingroup$

It looks like you're doing this in a very round-about manner. You said,

The matlab function in sumlink is supposed to read the received ASCII characters and add them to a variable until it reaches the end character '>'.

It looks like you are thinking the "signed numbers" are coming in as a string that you'll read one digit at a time, where the entire string is delimited by a start and stop magic number (the '<' and '>' symbols).

A couple of things I would point out:

  1. First and foremost, typically a signed integer is just sent as a byte or (more typically) a series of bytes. For example, a 16-bit signed integer (short signed integer) is composed of two 8-bit bytes. To convert to the signed 16-bit integer, just use the Matlab function int16. You're doing it the especially long and painful way of sending a string, with delimiters, then reading that string in one digit at a time, converting the characters to numbers, then multiplying the current number by 10 every time you want to concatenate a new digit.
  2. Probably where you're running into more trouble code-wise is that you're not using the Matlab persistent function correctly. From the documentation

If the persistent variable does not exist the first time you issue the persistent statement, it is initialized to the empty matrix.

This is the check you should be making. If the variable you want to be persistent is empty, then initialize it to zero. You are currently declaring it persistent and then immediately setting it to zero every function call!

Your code is:

persistent receivedNumber;
receivedNumber = 0;

when it should be:

persistent receivedNumber;
if isempty(receivedNumber)
    receivedNumber = 0;
end

This way, receivedNumber only gets set to zero IF IT IS EMPTY (meaning it was just created). In all other cases it should be retaining the value it had and thus would be NOT empty. For the record, you need to do the same thing for negative; they're both being implemented incorrectly.

Now, about your other question,

Can anybody tells if there are any other approaches to read multiple signed digits in simulink?

Yes, there are a couple things you could do. First, you could just concatenate the string and convert to double when you're done. That would look something like the following:

if(status ==1)
    switch(data)
        case EON
            output = str2double(receivedNumber);
        case SON
            receivedNumber = char;
        otherwise
            receivedNumber(end+1) = data;
    end
end

This code creates output by converting the running receivedNumber to a 'double' format when you hit the end of number delimiter. On receiving the start of number delimiter, output is reinitialized to an empty character array. Any other input appends whatever the current character is to the string.

In going through the code looking for your output variable, I noticed that you have Left and Right, which output your current receivedNumber. I'd point out that, generally speaking, receivedNumber is an intermediate result. I would create a persistent variable called output instead, assign Left and Right to the output, and then only update output when you have a full number to report.

You could further "bulletproof" the function by checking that the output of the str2double command is, in fact, a number, and that you didn't pick up a mis-transmitted byte somewhere along the way that would look something like -1Q65.

Right now you don't have any error checking in your inputs; if you get a mis-transmitted byte that is not a valid digit then you just skip handling it and then treat the next valid digit as correct. This is probably not what you should be doing; if, for example, you should have gotten -1065 and got -1Q65 instead, your code would output:

0;
1;
1;
16;
165;
-165;

The first zero is the output when you get '<', then you output 1 when you receive it, then you output 1 again because you don't handle Q, then you output 16, then 165, then you apply the negative sign when you hit the end delimiter >. The way I would suggest doing it instead would be:

persistent output;
persistent receivedNumber;
if isempty(output)
    output = 0;
    receivedNumber = char;
end
if status == 1
    switch(data)
        case '<'
            receivedNumber = char;
        case '>'
            tempResult = str2double(receivedNumber);
            if ~isnan(tempResult)
                output = tempResult;
            end
        otherwise
            receivedNumber(end+1) = data;
    end
end

Now your output is all zeros until the number is done being parsed, then it's the output number. The more important thing here is that, when you start receiving the next number, the output continues to be the previous number until the current number is done being parsed. If there is a misformed byte somewhere, the str2double conversion will output a NaN (Not A Number), and the ~isnan() (not isnan; Matlab uses ~ instead of !) check catches the mis-created number and the result is discarded.

Consider your previous code getting -1065, -1Q65, and then -1064. There's almost no difference there, so I wouldn't expect much response from your PID controller, but the way you're handling your feedback will give you nothing but headaches. You would get:

0;
1;
10;
106;
1065;
-1065;
0;
1;
1;
16;
165;
-165;
0;
1;
10;
106;
1064;
-1064;

There are 18 numbers there, only three of them have the correct sign, and only two of them are actually correct. Granted you were given a faulty input, but what should your system do in that circumstance? Error out, discard the input, or pass the faulty input as truth?

Whatever your decision on how an error should be handled, for control purposes, you should only be releasing complete and valid data.

:EDIT:

I just re-read my answer, and thought I'd reiterate that the persistent function isn't being used correctly. All of my examples of what your output would be are assuming you fix that. Otherwise, you code would output only whatever the current digit is.

$\endgroup$
5
  • 1
    $\begingroup$ Great answer! I would add a further warning, that the order in which the characters are sent from the device connected to the serial port has to match the assumptions that data is sent in the Most Significatn Digit -> Least Significant Digit order. $\endgroup$
    – 50k4
    Commented Dec 19, 2016 at 16:15
  • $\begingroup$ @50k4 - I agree, but endianness is always something that should be checked. OP is receiving the data in an unusual manner (as a string, one digit at a time vs. regular signed integers), so I'm assuming he/she has full control over what is being sent from the Arduino. Totally a valid point, though. $\endgroup$
    – Chuck
    Commented Dec 19, 2016 at 16:21
  • $\begingroup$ @Chuck Thanks a lot for your explanation. I had to work around this problem and make a step forward in processing from the sending side (BeagleBone in my case) and send them directly. I have tried your code and faced some problems regarding the index of the output in the last case. However, thank you for your specified answer :) $\endgroup$
    – DieDen
    Commented Dec 20, 2016 at 16:40
  • $\begingroup$ @DieDen - Sorry, just re-read the code and realized I wrote the wrong variable in the third case. I'll fix it now. $\endgroup$
    – Chuck
    Commented Dec 20, 2016 at 16:45
  • $\begingroup$ @DieDen - Fixed the problem. I originally wrote output(end+1) = data, which is wrong because output is the output variable; receivedNumber is the intermediate result. I changed it to receivedNumber(end+1) = data, which should fix the indexing problem. $\endgroup$
    – Chuck
    Commented Dec 20, 2016 at 16:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.