This example shows how to use message communication within a distributed system where the controller manages multiple incoming messages from different senders in an iterative manner and sends messages to communicate commands to the different receivers. The example uses a model of a control system managing temperatures in two different rooms with separate thermostats. The algorithmic modeling of the components basically follows the Stateflow example Model Bang-Bang Temperature Control System (Stateflow), while the communication between the components is modeled using Simulink® messages and SimEvents® blocks. The referenced models Controller and Thermometer, colored in blue, are software components expected to generate standalone code, while the other components model the environment.
The model contains
N identical rooms with thermostats (modeled by multi-instanced model blocks), where
2 is a Simulink parameter defined in the Simulink data dictionary file
slddMsg.sldd, which is linked to the top model and referenced models. Each room can set the setpoint temperature separately. The thermostats for the rooms are remotely controlled by a controller, using the same control algorithm for all thermostats.
The thermostats send temperature messages to the controller every
0.2 seconds, while the controller sends command messages to the thermostats to command heating on or off every
1 second. An Entity Output Switch (SimEvents) block routes the messages from the controller to one of the thermostats according to the message bus data field
deviceID. (The bus is also defined in the data dictionary and shared across all models.) A Message Merge block routes the messages from different thermostats to the controller.
Initially, the model is configured using bus types with floating-point type fields that are defined in the data dictionary file
slddMsg.sldd. To switch from floating-point to fixed-point, change the data dictionary for the model from
slddMsgFixpt.sldd. On the Modeling tab, go to Model Settings > Model Properties > External Data.
The model is easily scalable by changing the value of
N, adding more instances of the model block, and increasing the port number of the Entity Output Switch and Message Merge blocks. Each Thermometer model inside the Room model has an
ID argument, which must be set with a value that matches the output port index of the Entity Output Switch.
A Queue block (FIFO, overwriting type queue) in front of the controller model buffers the message, which models the queue inside the message middleware of the controller. Here, a capacity of
N is good enough for the queue to overwrite the oldest messages with the new ones from each sender, assuming no message loss in transit. A capacity of
5*N is needed for the worst scenario with message loss, where
5 is the sample time of the controller divided by the sample time of the thermostats. In addition, a queue in front of each thermostat with capacity 1 is automatically inserted and shows a badge icon of sandwiched "1", because a capacity-1 queue is automatically inserted if you do not intentionally place a Queue block. See Use a Queue Block to Manage Messages.
To view the sequences of messages and events, on the Simulink Toolstrip, on the Simulation tab, in the Review Results section, click Sequence Viewer. See Sequence Viewer.
In the Controller model, the Update Temperature subsystem connected with the Inport block first receives all messages containing temperature information from the rooms. The subsystem stores that information in two vectors of temperature setpoint and current temperature. Then, the For Each subsystem reads the vectors, processes the signals, and sends out the control messages via the Simulink function
The Update Temperature subsystem is a do-while subsystem whose termination condition port is fed by the Receive block's status port, meaning it runs until cannot receive any more messages from the external queue (in the top model). The message data is of
DeviceMsg bus type, which is defined in the data dictionary file, and has two fields:
deviceID. Thus, when the output signal of the Receive block propagates to the enabled subsystem whose enable port is connected to the Receive block's status port, the Bus Selector block decomposes the signal into
setpoint signals. The
temperature signals are then assigned to the respective vector elements associated with the
deviceID. Finally, the vectors maintained by the Unit Delay blocks are output as signals by the enabled subsystem and Update Temperature subsystem to the For Each subsystem.
The For Each subsystem, whose block settings are shown above, is set to have
N iterations, and both of its input ports are partitioned. A Stateflow chart models the Bang-Bang Controller, which resembles the one explained in Model Bang-Bang Temperature Control System (Stateflow). Its output port outputs a Boolean signal indicating whether or not to turn on heating. This signal is packed into a nonvirtual signal at the Bus Creator block with the deviceID (one-based) from the iteration number (zero-based). The signal is given to the Function Caller block, which calls the Simulink Function
SendCtrlMsg (placed outside the For Each Subsystem) to send the message out from the model.
In the Room model, the Thermostat subsystem interacts with the environment. The Thermostat has two inputs, the control message and the setpoint temperature signal, and two outputs, the heating rate and the temperature message to the controller. The Gain and Integrator blocks simulate the physics of the room heating or cooling with respect to the heating rate and room size.
The Thermostat subsystem is composed of a Thermometer Sensor subsystem, a Thermometer Software model block, and a Temperature Actuator subsystem. The Thermometer Software model block periodically receives a control message from the controller and unpacks it into a Boolean command (on/off) to the Temperature Actuator subsystem, which determines the heating rate. The Thermometer Software also inputs a temperature signal from Thermometer Sensor subsystem, which detects the analog temperature, converts it to a digital signal, and sends the message back to the controller.
In the Thermometer model, a Receive block connects with the Inport block to receive a control message from the external queue at each time step. The message data is decomposed into a command signal, which is an output, and a deviceID signal, which must match the ID argument of the model. The ID argument should be set in the model block in the top model. The Receive block's initial value is set to a MATLAB® structure with the
deviceID field equal to the model argument
ID and the
command field taking a value of
false. Meanwhile, the signals of digital temperature, setpoint, and deviceID are packed into a nonvirtual bus signal and sent as a message to the Outport block.
For code generation and deployment, the referenced models Controller and Thermometer (colored blue) can generate standalone embedded-target C++ code and can be deployed separately on embedded devices with message middleware. For more information, see Generate C++ Messages to Communicate Between Simulink and an Operating System or Middleware (Embedded Coder); see also Use Handwritten Code to Integrate C++ Messages with POSIX (Embedded Coder).
Message root-level Inport/Outport does not support C code generation and code customization. If you need to generate C code and call into a message middleware API for sending messages, consider moving the Simulink function
sendCtrlMsg to the top model and customizing the name properly so that the referenced model generates a customizable call site of an external function. Similarly, for the receive side, consider using a Simulink function containing a Receive block in the top model and using a Function Caller block in the referenced model to replace the Receive block.