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/drivers/gpio.h>
11 #include <zephyr/modbus/modbus.h>
12 
13 #include <zephyr/posix/netinet/in.h>
14 #include <zephyr/posix/sys/socket.h>
15 #include <zephyr/posix/arpa/inet.h>
16 #include <zephyr/posix/unistd.h>
17 #include <zephyr/posix/poll.h>
18 #include <zephyr/posix/netdb.h>
19 
20 #include <zephyr/net/socket.h>
21 
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(tcp_modbus, LOG_LEVEL_INF);
24 
25 #define MODBUS_TCP_PORT 502
26 
27 static uint16_t holding_reg[8];
28 static uint8_t coils_state;
29 
30 static const struct gpio_dt_spec led_dev[] = {
31 	GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios),
32 	GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios),
33 	GPIO_DT_SPEC_GET(DT_ALIAS(led2), gpios),
34 };
35 
36 static int custom_read_count;
37 
custom_handler(const int iface,const struct modbus_adu * rx_adu,struct modbus_adu * tx_adu,uint8_t * const excep_code,void * const user_data)38 static bool custom_handler(const int iface,
39 			   const struct modbus_adu *rx_adu,
40 			   struct modbus_adu *tx_adu,
41 			   uint8_t *const excep_code,
42 			   void *const user_data)
43 {
44 	const uint8_t request_len = 2;
45 	const uint8_t response_len = 6;
46 	int *read_counter = (int *)user_data;
47 	uint8_t subfunc;
48 	uint8_t data_len;
49 
50 	LOG_INF("Custom Modbus handler called");
51 
52 	if (rx_adu->length != request_len) {
53 		LOG_WRN("Custom request length doesn't match");
54 		*excep_code = MODBUS_EXC_ILLEGAL_DATA_VAL;
55 		return true;
56 	}
57 
58 	subfunc = rx_adu->data[0];
59 	data_len = rx_adu->data[1];
60 
61 	LOG_INF("Custom function called with subfunc=%u, data_len=%u", subfunc, data_len);
62 	(*read_counter)++;
63 	sys_put_be16(0x5555, tx_adu->data);
64 	sys_put_be16(0xAAAA, &tx_adu->data[2]);
65 	sys_put_be16(*read_counter, &tx_adu->data[4]);
66 	tx_adu->length = response_len;
67 
68 	return true;
69 }
70 
71 MODBUS_CUSTOM_FC_DEFINE(custom, custom_handler, 101, &custom_read_count);
72 
init_leds(void)73 static int init_leds(void)
74 {
75 	int err;
76 
77 	for (int i = 0; i < ARRAY_SIZE(led_dev); i++) {
78 		if (!gpio_is_ready_dt(&led_dev[i])) {
79 			LOG_ERR("LED%u GPIO device not ready", i);
80 			return -ENODEV;
81 		}
82 
83 		err = gpio_pin_configure_dt(&led_dev[i], GPIO_OUTPUT_INACTIVE);
84 		if (err != 0) {
85 			LOG_ERR("Failed to configure LED%u pin", i);
86 			return err;
87 		}
88 	}
89 
90 	return 0;
91 }
92 
coil_rd(uint16_t addr,bool * state)93 static int coil_rd(uint16_t addr, bool *state)
94 {
95 	if (addr >= ARRAY_SIZE(led_dev)) {
96 		return -ENOTSUP;
97 	}
98 
99 	if (coils_state & BIT(addr)) {
100 		*state = true;
101 	} else {
102 		*state = false;
103 	}
104 
105 	LOG_INF("Coil read, addr %u, %d", addr, (int)*state);
106 
107 	return 0;
108 }
109 
coil_wr(uint16_t addr,bool state)110 static int coil_wr(uint16_t addr, bool state)
111 {
112 	bool on;
113 
114 	if (addr >= ARRAY_SIZE(led_dev)) {
115 		return -ENOTSUP;
116 	}
117 
118 	if (state == true) {
119 		coils_state |= BIT(addr);
120 		on = true;
121 	} else {
122 		coils_state &= ~BIT(addr);
123 		on = false;
124 	}
125 
126 	gpio_pin_set(led_dev[addr].port, led_dev[addr].pin, (int)on);
127 
128 	LOG_INF("Coil write, addr %u, %d", addr, (int)state);
129 
130 	return 0;
131 }
132 
holding_reg_rd(uint16_t addr,uint16_t * reg)133 static int holding_reg_rd(uint16_t addr, uint16_t *reg)
134 {
135 	if (addr >= ARRAY_SIZE(holding_reg)) {
136 		return -ENOTSUP;
137 	}
138 
139 	*reg = holding_reg[addr];
140 
141 	LOG_INF("Holding register read, addr %u", addr);
142 
143 	return 0;
144 }
145 
holding_reg_wr(uint16_t addr,uint16_t reg)146 static int holding_reg_wr(uint16_t addr, uint16_t reg)
147 {
148 	if (addr >= ARRAY_SIZE(holding_reg)) {
149 		return -ENOTSUP;
150 	}
151 
152 	holding_reg[addr] = reg;
153 
154 	LOG_INF("Holding register write, addr %u", addr);
155 
156 	return 0;
157 }
158 
159 static struct modbus_user_callbacks mbs_cbs = {
160 	.coil_rd = coil_rd,
161 	.coil_wr = coil_wr,
162 	.holding_reg_rd = holding_reg_rd,
163 	.holding_reg_wr = holding_reg_wr,
164 };
165 
166 static struct modbus_adu tmp_adu;
167 K_SEM_DEFINE(received, 0, 1);
168 static int server_iface;
169 
server_raw_cb(const int iface,const struct modbus_adu * adu,void * user_data)170 static int server_raw_cb(const int iface, const struct modbus_adu *adu,
171 			void *user_data)
172 {
173 	LOG_DBG("Server raw callback from interface %d", iface);
174 
175 	tmp_adu.trans_id = adu->trans_id;
176 	tmp_adu.proto_id = adu->proto_id;
177 	tmp_adu.length = adu->length;
178 	tmp_adu.unit_id = adu->unit_id;
179 	tmp_adu.fc = adu->fc;
180 	memcpy(tmp_adu.data, adu->data,
181 	       MIN(adu->length, CONFIG_MODBUS_BUFFER_SIZE));
182 
183 	LOG_HEXDUMP_DBG(tmp_adu.data, tmp_adu.length, "resp");
184 	k_sem_give(&received);
185 
186 	return 0;
187 }
188 
189 const static struct modbus_iface_param server_param = {
190 	.mode = MODBUS_MODE_RAW,
191 	.server = {
192 		.user_cb = &mbs_cbs,
193 		.unit_id = 1,
194 	},
195 	.rawcb.raw_tx_cb = server_raw_cb,
196 	.rawcb.user_data = NULL
197 };
198 
init_modbus_server(void)199 static int init_modbus_server(void)
200 {
201 	char iface_name[] = "RAW_0";
202 	int err;
203 
204 	server_iface = modbus_iface_get_by_name(iface_name);
205 
206 	if (server_iface < 0) {
207 		LOG_ERR("Failed to get iface index for %s",
208 			iface_name);
209 		return -ENODEV;
210 	}
211 
212 	err = modbus_init_server(server_iface, server_param);
213 
214 	if (err < 0) {
215 		return err;
216 	}
217 
218 	return modbus_register_user_fc(server_iface, &modbus_cfg_custom);
219 }
220 
modbus_tcp_reply(int client,struct modbus_adu * adu)221 static int modbus_tcp_reply(int client, struct modbus_adu *adu)
222 {
223 	uint8_t header[MODBUS_MBAP_AND_FC_LENGTH];
224 
225 	modbus_raw_put_header(adu, header);
226 	if (send(client, header, sizeof(header), 0) < 0) {
227 		return -errno;
228 	}
229 
230 	if (send(client, adu->data, adu->length, 0) < 0) {
231 		return -errno;
232 	}
233 
234 	return 0;
235 }
236 
modbus_tcp_connection(int client)237 static int modbus_tcp_connection(int client)
238 {
239 	uint8_t header[MODBUS_MBAP_AND_FC_LENGTH];
240 	int rc;
241 	int data_len;
242 
243 	rc = recv(client, header, sizeof(header), MSG_WAITALL);
244 	if (rc <= 0) {
245 		return rc == 0 ? -ENOTCONN : -errno;
246 	}
247 
248 	LOG_HEXDUMP_DBG(header, sizeof(header), "h:>");
249 	modbus_raw_get_header(&tmp_adu, header);
250 	data_len = tmp_adu.length;
251 
252 	rc = recv(client, tmp_adu.data, data_len, MSG_WAITALL);
253 	if (rc <= 0) {
254 		return rc == 0 ? -ENOTCONN : -errno;
255 	}
256 
257 	LOG_HEXDUMP_DBG(tmp_adu.data, tmp_adu.length, "d:>");
258 	if (modbus_raw_submit_rx(server_iface, &tmp_adu)) {
259 		LOG_ERR("Failed to submit raw ADU");
260 		return -EIO;
261 	}
262 
263 	if (k_sem_take(&received, K_MSEC(1000)) != 0) {
264 		LOG_ERR("MODBUS RAW wait time expired");
265 		modbus_raw_set_server_failure(&tmp_adu);
266 	}
267 
268 	return modbus_tcp_reply(client, &tmp_adu);
269 }
270 
main(void)271 int main(void)
272 {
273 	int serv;
274 	struct sockaddr_in bind_addr;
275 	static int counter;
276 
277 	if (init_modbus_server()) {
278 		LOG_ERR("Modbus TCP server initialization failed");
279 		return 0;
280 	}
281 
282 	if (init_leds()) {
283 		LOG_ERR("Modbus TCP server initialization failed");
284 		return 0;
285 	}
286 
287 	serv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
288 
289 	if (serv < 0) {
290 		LOG_ERR("error: socket: %d", errno);
291 		return 0;
292 	}
293 
294 	bind_addr.sin_family = AF_INET;
295 	bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
296 	bind_addr.sin_port = htons(MODBUS_TCP_PORT);
297 
298 	if (bind(serv, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) < 0) {
299 		LOG_ERR("error: bind: %d", errno);
300 		return 0;
301 	}
302 
303 	if (listen(serv, 5) < 0) {
304 		LOG_ERR("error: listen: %d", errno);
305 		return 0;
306 	}
307 
308 	LOG_INF("Started MODBUS TCP server example on port %d", MODBUS_TCP_PORT);
309 
310 	while (1) {
311 		struct sockaddr_in client_addr;
312 		socklen_t client_addr_len = sizeof(client_addr);
313 		char addr_str[INET_ADDRSTRLEN];
314 		int client;
315 		int rc;
316 
317 		client = accept(serv, (struct sockaddr *)&client_addr,
318 				&client_addr_len);
319 
320 		if (client < 0) {
321 			LOG_ERR("error: accept: %d", errno);
322 			continue;
323 		}
324 
325 		inet_ntop(client_addr.sin_family, &client_addr.sin_addr,
326 			  addr_str, sizeof(addr_str));
327 		LOG_INF("Connection #%d from %s",
328 			counter++, addr_str);
329 
330 		do {
331 			rc = modbus_tcp_connection(client);
332 		} while (!rc);
333 
334 		close(client);
335 		LOG_INF("Connection from %s closed, errno %d",
336 			addr_str, rc);
337 	}
338 	return 0;
339 }
340