1 /*
2 * Copyright (c) 2020 PHYTEC Messtechnik GmbH
3 * Copyright (c) 2021 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/kernel.h>
9 #include <zephyr/sys/util.h>
10 #include <zephyr/modbus/modbus.h>
11 #include <zephyr/net/socket.h>
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(tcp_gateway, LOG_LEVEL_INF);
15
16 #define MODBUS_TCP_PORT 502
17
18 static struct modbus_adu tmp_adu;
19 static int backend;
20
21 const static struct modbus_iface_param backend_param = {
22 .mode = MODBUS_MODE_RTU,
23 .rx_timeout = 50000,
24 .serial = {
25 .baud = 19200,
26 .parity = UART_CFG_PARITY_NONE,
27 },
28 };
29
30 #define MODBUS_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(zephyr_modbus_serial)
31
init_backend_iface(void)32 static int init_backend_iface(void)
33 {
34 const char bend_name[] = {DEVICE_DT_NAME(MODBUS_NODE)};
35
36 backend = modbus_iface_get_by_name(bend_name);
37 if (backend < 0) {
38 LOG_ERR("Failed to get iface index for %s",
39 bend_name);
40 return -ENODEV;
41 }
42
43 return modbus_init_client(backend, backend_param);
44 }
45
modbus_tcp_reply(int client,struct modbus_adu * adu)46 static int modbus_tcp_reply(int client, struct modbus_adu *adu)
47 {
48 uint8_t header[MODBUS_MBAP_AND_FC_LENGTH];
49
50 modbus_raw_put_header(adu, header);
51 if (send(client, header, sizeof(header), 0) < 0) {
52 return -errno;
53 }
54
55 if (send(client, adu->data, adu->length, 0) < 0) {
56 return -errno;
57 }
58
59 return 0;
60 }
61
modbus_tcp_connection(int client)62 static int modbus_tcp_connection(int client)
63 {
64 uint8_t header[MODBUS_MBAP_AND_FC_LENGTH];
65 int rc;
66 int data_len;
67
68 rc = recv(client, header, sizeof(header), MSG_WAITALL);
69 if (rc <= 0) {
70 return rc == 0 ? -ENOTCONN : -errno;
71 }
72
73 LOG_HEXDUMP_DBG(header, sizeof(header), "h:>");
74 modbus_raw_get_header(&tmp_adu, header);
75 data_len = tmp_adu.length;
76
77 rc = recv(client, tmp_adu.data, data_len, MSG_WAITALL);
78 if (rc <= 0) {
79 return rc == 0 ? -ENOTCONN : -errno;
80 }
81
82 LOG_HEXDUMP_DBG(tmp_adu.data, tmp_adu.length, "d:>");
83 rc = modbus_raw_backend_txn(backend, &tmp_adu);
84 if (rc == -ENOTSUP || rc == -ENODEV) {
85 LOG_WRN("Backend interface error: %d", rc);
86 }
87
88 return modbus_tcp_reply(client, &tmp_adu);
89 }
90
main(void)91 int main(void)
92 {
93 int serv;
94 struct sockaddr_in bind_addr;
95 static int counter;
96
97 if (init_backend_iface()) {
98 LOG_ERR("Modbus initialization failed");
99 return 0;
100 }
101
102 serv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
103
104 if (serv < 0) {
105 LOG_ERR("error: socket: %d", errno);
106 return 0;
107 }
108
109 bind_addr.sin_family = AF_INET;
110 bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
111 bind_addr.sin_port = htons(MODBUS_TCP_PORT);
112
113 if (bind(serv, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) < 0) {
114 LOG_ERR("error: bind: %d", errno);
115 return 0;
116 }
117
118 if (listen(serv, 5) < 0) {
119 LOG_ERR("error: listen: %d", errno);
120 return 0;
121 }
122
123 LOG_INF("Started MODBUS TCP gateway example on port %d", MODBUS_TCP_PORT);
124
125 while (1) {
126 struct sockaddr_in client_addr;
127 socklen_t client_addr_len = sizeof(client_addr);
128 char addr_str[INET_ADDRSTRLEN];
129 int client;
130 int rc;
131
132 client = accept(serv, (struct sockaddr *)&client_addr,
133 &client_addr_len);
134
135 if (client < 0) {
136 LOG_ERR("error: accept: %d", errno);
137 continue;
138 }
139
140 inet_ntop(client_addr.sin_family, &client_addr.sin_addr,
141 addr_str, sizeof(addr_str));
142 LOG_INF("Connection #%d from %s",
143 counter++, addr_str);
144
145 do {
146 rc = modbus_tcp_connection(client);
147 } while (!rc);
148
149 close(client);
150 LOG_INF("Connection from %s closed, errno %d",
151 addr_str, rc);
152 }
153 return 0;
154 }
155