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 N
= 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 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 the command messages to the thermostats to command heating on or off every 1
second. An Entity Output Switch (SimEvents) block routes the controller's messages to one of the thermostats according to the message bus data field deviceID
(the bus is also defined in slddMsg.sldd
and shared across all models). An Entity Input Switch (SimEvents) block routes the messages from different thermostats to the controller.
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 Entity Input Switch 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 as long as no messages from the thermostats are dropped in the transport. 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, the 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.
Double click the Sequence Viewer block to view the sequences of messages and events.
In the Controller model, the Update Temperature subsystem connected with the Inport block first receives all messages containing the temperature information from 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 message via the Simulink Function sendCtrlMsg
.
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 (defined in slddMsg.sldd
), which has two fields: temperature
and deviceID
. Thus, when the output signal of the Receive block propagates to the Enable subsystem whose enable port is connected to the Receive block's status port, the Bus Selector block decomposes the signal into deviceID
, temperature
, and setpoint
signals. The setpoint
and 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 Enable 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 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.
For Each Subsystem | Function Caller | Gain | Integrator | Receive | Simulink Function