How to send double values over TCP/IP in Simulink Support Package for Raspberry Pi

646 views Asked by At

Is it possible to send double values using the TCP/IP client block in the Simulink Support Package for Raspberry Pi?

I have been stuck for a while trying to get this to work: enter image description here

but using printf("%f %f %f", *((double *)tcp_buffer)), *((double *)tcp_buffer + 1)), *((double *)tcp_buffer + 2))) only prints zeros.

This, however, works ok (printing an int):

enter image description here

1

There are 1 answers

0
Tommy Wolfheart On

After trying to get this working for quite a while, I am under the impression that it will not work (or there is a rather obscured way of doing it). I wound up implementing my own TCP/IP client device driver (i.e. a Simulink block similar to the ones provided by the package) to do the trick and thought I'd leave my implementation here for anyone that's looking. (I think it's better that way anyway as you can really define how you want your client to work.)

To create the device driver, one would need to follow the instructions found on the MATHWORKS site here.

My C implementation for the client is as follows:

/* File: client.h */
#ifndef _CLIENT_H_
#define _CLIENT_H_
#include "rtwtypes.h"

extern int socket_desc;

void init_socket_comm(void);
void send_socket_data(void);
void close_socket(void);

#endif /* _CLIENT_H */

and

/* File: client.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>

#include "client.h"

int socket_desc;

int_T init_socket_comm(void)
{
    struct sockaddr_in server; /* Server socket parameters */

    /* Create socket and get socket descriptor */
    socket_desc = socket(AF_INET, SOCK_STREAM, 0);

    if (socket_desc < 0)
    {
        fprintf(stderr, "Could not create socket\n");
        exit(EXIT_FAILURE);
    }

    /* Specify parameters for server socket */
    server.sin_addr.s_addr = inet_addr("127.0.0.1"); /* Server TCP IP;
    server.sin_family = AF_INET;
    server.sin_port = htons(25000); /* TCP port */;

    /* Connect socket to server */
    if (connect(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0)
    {
        fprintf(stderr, "Could not connect to server\n");

        /* Close socket */
        close(socket_desc);
        printf("Socket closed successfully.\n");

        exit(EXIT_FAILURE);
    }

    return socket_desc;
}

/* Sends an array of doubles. */
void send_socket_data(void)
{
    int buffer_size = 56;
    real_T buffer[7] = {3.21232, 5.31453, 10.34108, 7.34652, 2.34524, 4.44432, 3.45642};

    unsigned char *buffer_ptr = (unsigned char *)buffer;
    int sent_size;

    /* Send data to server */
    while (buffer_size > 0)
    {
        if ((sent_size = send(socket_desc, buffer, buffer_size, 0)) < 0)
        {
            fprintf(stderr, "Send failed.\n");

            /* Close socket */
            close(socket_desc);
            printf("Socket closed successfully.\n");

            exit(EXIT_FAILURE);
        }

        buffer_ptr += sent_size;
        buffer_size -= sent_size;
    }
}

void close_socket(void)
{
    /* Close socket */
    close(socket_desc);
}

Next, you'd need you System Object (I named mine TcpClient):

% File: TcpClient.m
classdef TcpClient < matlab.System & coder.ExternalDependency
    %
    % System object TcpClient block.
    
    properties
        % Public, tunable properties.
    end
    
    properties (Nontunable)
        % Public, non-tunable properties.
    end
    
    properties (Access = private)
        % Pre-computed constants.
    end
    
    methods
        % Constructor
        function obj = TcpClient(varargin)
            % Support name-value pair arguments when constructing the object.
            setProperties(obj,nargin,varargin{:});
        end
    end
    
    methods (Access=protected)
        function setupImpl(obj) %#ok<MANU>
            if isempty(coder.target)
                % Place simulation setup code here
            else
                % Call C-function implementing device initialization
                coder.cinclude('client.h');
                coder.ceval('init_socket_comm');
            end
        end
        
        function stepImpl(obj,u)  %#ok<INUSD>
            if isempty(coder.target)
                % Place simulation output code here 
            else
                % Call C-function implementing device output
                coder.ceval('send_socket_data');
            end
        end
        
        function releaseImpl(obj) %#ok<MANU>
            if isempty(coder.target)
                % Place simulation termination code here
            else
                % Call C-function implementing device termination
                coder.ceval('close_socket');
            end
        end
    end
    
    methods (Access=protected)
        %% Define input properties
        function num = getNumInputsImpl(~)
            num = 1;
        end
        
        function num = getNumOutputsImpl(~)
            num = 0;
        end
        
        function flag = isInputSizeMutableImpl(~,~)
            flag = false;
        end
        
        function flag = isInputComplexityMutableImpl(~,~)
            flag = false;
        end
        
        function validateInputsImpl(~, u)
            if isempty(coder.target)
                % Run input validation only in Simulation
                validateattributes(u,{'double'},{'scalar'},'','u');
            end
        end
        
        function icon = getIconImpl(~)
            % Define a string as the icon for the System block in Simulink.
            icon = 'Sink';
        end
    end
    
    methods (Static, Access=protected)
        function simMode = getSimulateUsingImpl(~)
            simMode = 'Interpreted execution';
        end
        
        function isVisible = showSimulateUsingImpl
            isVisible = false;
        end
    end
    
    methods (Static)
        function name = getDescriptiveName()
            name = 'Sink';
        end
        
        function b = isSupportedContext(context)
            b = context.isCodeGenTarget('rtw');
        end
        
        function updateBuildInfo(buildInfo, context)
            if context.isCodeGenTarget('rtw')
                % Update buildInfo
                srcDir = fullfile(fileparts(mfilename('fullpath')),'src'); %#ok<NASGU>
                includeDir = fullfile(fileparts(mfilename('fullpath')),'include');
                addIncludePaths(buildInfo,includeDir);
                % Use the following API's to add include files, sources and
                addSourceFiles(buildInfo,'client.c', srcDir);       
            end
        end
    end
end

Now you can send doubles and if you create your server correctly using the code from here which I adapted to create a function that reads the TCP buffer:

/* File: server.c */
int read_socket_data(int new_socket, void *buffer, int buffer_size)
{
    int read_size;
    int read_attempts;

    /* Continously read TCP buffer */
    while ((read_size = recv(new_socket, buffer, buffer_size, 0)) >= 0)
    {
        if (read_size > 0)
        {
            /* Print read buffer */
            for (int offset = 0; offset < 7; offset++)
            {
                printf("%lf\n", *((double *)buffer + offset));
            }
        }
    }

    return read_size;
}

you can read and print the doubles correctly.