If you're using Matlab to connect to a remote device, like an Arduino or Cognex device, then you're probably using the TCP support of Matlab's Instrument Control Toolbox. Integrating Matlab with devices may be the first or only time you've written TCP code. This post will provide you with information on TCP and example Matlab code you can take and use—it's been tested on Matlab R2025a. After reading this, you'll be able to easily connect Matlab to devices over TCP sockets.
Transmission Control Protocol (TCP) is used to transfer data over a network. A TCP connection passes data between your computer and another. Either machine can read or write to the other and they can do this simultaneously; TCP is bidirectional and full-duplex. When one side writes a byte, the other side will need to read it out to use it.
The two sides of a TCP connection are each identified with a socket address: a pair containing an IP address and a port number. The IP address is used to identify the machine. The port number allows multiple TCP connections between the same machines; different connections use different port numbers.
When you opened this web page, you started a TCP connection. This website runs on a machine with IP address 159.223.188.121
and listens on port 443
. When your web browser connects to this web page, it refers to the website IP address and port 443. Your computer has its own IP address and uses a random port number called an ephemeral port number. The ephemeral port is different than the server port. Internally, every TCP message contains a source socket address and a destination socket address.
TCP is a stream based protocol. With TCP, you may only get a single byte at a time. If you send 4,096 bytes over a TCP connection, they may be broken up into smaller messages or grouped with a larger message. The client may get messages of arbitrary size.
Usually, TCP interfaces allow a client to read a block of bytes at a time, but there's no gurantee you'll get the amount of bytes you attempt to read; The client can wait for more bytes, but there's no gurantee that many bytes will arrive. What if nothing else is sent? For this reason, almost every message protocol will include some information to help frame the data. For example, messages may end with line breaks.
Matlab has built-in support to make line break terminated messages easy to read. In the code below, The tcplisten
function is called with the port number 8080. It will start listening with the IP address of the machine it runs on and the port number it was given. The server is listening so it will wait for a connection from another machine. The "LF"
sets the terminator to '\n'
: the newline character. The most complicated part—and it's still straightforward—is the callback. The function readData
is set as a callback. The callback function gets called whenever the terminator appears. The callback doesn't take the message as an argument, but the connection instead. You still have to call readline
to get the message string.
function readData(connection,~)
message = readline(connection)
disp(message)
end
Server = tcpserver("::", 8080)
Server.configureTerminator("LF")
Server.configureCallback("terminator", @readData)
We can test the server with telnet
or nc
. We connect to our TCP server and type messages in. Our server will read in and display the messasges we send.
$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
$ nc localhost 8080
hello
Either way we'll see the same result:
message =
"hello"
hello
Sometimes the device frames the bytes between a start and end byte, like a (
and )
pair or an ASCII STX
and ETX
. Matlab doesn't have builtin support for this, but we can set it up with a simple state machine. We wait for a start byte, and save the bytes we read in until we get a closing byte.
The state machine below shows a circle for each state and a transition for each incoming byte. We want our code to start in the WAITING_ON_OPEN
state. In that state, it should check for an opening bracket [
and ignore any other byte. Once it gets a [
our code to start checking for a closing bracket ]
and save any other byte. Once the gets a ]
, we reset the state machine--by changing the state to WAITING_ON_OPEN
again, and pass the bytes we saved to a function that handles full messages.
Below, we implement the state machine. The updateState
function uses persistent variables to track the current state and any saved bytes. When a full message is received, it's converted from a list of bytes into a string and then passed to the onFullMessage
function.
function onFullMessage(Message)
disp(Message)
end
function updateState(Byte)
persistent State;
persistent MessageBuffer;
if isempty(State)
State = "WAITING_ON_OPEN"
MessageBuffer = []
end
if strcmp(State, "WAITING_ON_OPEN")
if Byte == uint8('[')
State = "READING";
end
elseif strcmp(State, "READING")
if Byte == uint8(']')
State = "WAITING_ON_OPEN";
onFullMessage(char(MessageBuffer));
MessageBuffer = [];
else
MessageBuffer(end + 1) = Byte;
end
end
end
Now we can set a callback for reading data and start a TCP server. Each time we read in a byte we send it to the state machine to update it. Just like the code for newline terminated messages, the callback doesn't receives the connection. We have to call read within the connection to get the byte.
function readData(connection,~)
byte = read(connection, 1);
updateState(byte);
end
Server = tcpserver("::", 8080)
configureCallback(Server, "byte", 1, @readData)
We can test the code by sending a message over nc
.
$ nc localhost 8080
[hello]
Now, if your device is looking for a TCP server to connect to you have notes and code to help you with your solution.
This blog demonstrates how to set up a TCP server for a single device, but if you want multiple devices on the same port you won't be able to use Matlab's tcpserver
. You'll have to use something like Matlab's Java interoperability support. Climbing Computer Company provides software that can easily handle hundreds of devices and runs in Matlab. If this could help you, please check out our product page.