Creating Serial COMM-Port Simulator using Socat
In this post, we will talk about Creating a Serial COMM-Port Simulator using Socat. Socat stands for SOcket CAT, its utility to transfer data between two channels with both directions.
There are many different types of channels socat can connect, including:
Files
Pipes
Devices (serial line, pseudo-terminal, etc)
Sockets (UNIX, IP4, IP6 - raw, UDP, TCP)
SSL sockets
Proxy CONNECT connections
File descriptors (stdin, etc)
The GNU line editor (readline)
Programs
Combinations of two of these
For more information about Socat you can find it on its documentations (http://www.dest-unreach.org/socat/)
First of all, prepare Socat command to be executed by PrinterSimulator
socat -d -d pty,raw,echo=0,link=/tmp/ttyPrinter tcp=127.0.0.1:3006,retry=5
Explanation
socat: the socat command
-d -d : Print fatal, error, warning, notice logs
pty: Generates a pseudo terminal (pty) and uses its master side
raw: Sets raw mode, thus passing input and output almost unprocessed
echo=0: disable local echo
link=/tmp/ttyPrinter : Generates a symbolic link that points to the actual pseudo terminal (pty). Without this, socat will generate pty with sequence name like: /dev/pts/1
tcp=172.0.0.1:3006 : forward data to this (simulator) address
retry=5: Number of retries before the connection or listen attempt is aborted. Default is 0, which means just one attempt.
After specifying the socat command, we are going to create TCP Server on PrinterSimulator. This is going to be a simple SocketServer that will always return success.
public class PrinterSimulatorTcp {
public static void main(String[] args) {
PrinterSimulatorTcp instance = new PrinterSimulatorTcp();
instance.startSimulator();
new Thread( () -> {
MainSimulator.executeShellCommand("socat -dd pty,raw,echo=0,link=/tmp/ttyPrinter tcp:127.0.0.1:3006,retry=5");
}).start();
}
public void startSimulator() {
System.out.println("STARTING TCP 3006");
new Thread( () -> {
try (ServerSocket server = new ServerSocket(3006))
{
try (
Socket socket = server.accept();
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream()
) {
while (true) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] read = new byte[1024];
int len = in.read(read);
System.out.println("INCOMMING MESSAGE");
baos.write(read, 0, len);
byte[] bytes = baos.toByteArray();
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
System.out.println(sb);
if (sb.toString().startsWith("10"))
out.write(new byte[]{0x12});
}
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}).start();
}
public static String executeShellCommand(String command)
{
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(new String[]{"bash", "-c", command});
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null)
{
output.append(line + "\n");
}
System.out.println(output.toString());
return output.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
- we have three methods called executeShellCommand and startSimulator and main method.
- startSimulator will start tcp server on port 3006 and waiting for incomming message with method in.read(read) after message received, it will check the content start with "10" (hex string), it will response "12" (hex)
- executeShellCommand will execute socat command, socat will forward incomming command in the serial port /tmp/ttyPrinter to 127.0.0.1:3006
- response from our simulator will be forwared back to the serial port.
- main method used to execute socat command, and tcp server on different thread.
echo -en '\x10\x02\x02\x02' > /tmp/ttyPrinter
above command will sending data "10 02 02 02" to serial COMM-Port /tmp/ttyPrinter
Results:
Comments
Post a Comment