1 /*
2  * Copyright (c) 2020 PHYTEC Messtechnik GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "test_modbus.h"
8 
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(mbc_test, LOG_LEVEL_INF);
11 
12 #ifdef CONFIG_MODBUS_CLIENT
13 const static uint16_t fp_offset = MB_TEST_FP_OFFSET;
14 const static uint8_t node = MB_TEST_NODE_ADDR;
15 const static uint16_t offset_oor = 32;
16 const static uint16_t fp_offset_oor = fp_offset + offset_oor;
17 
18 static uint8_t client_iface;
19 
test_get_client_iface(void)20 uint8_t test_get_client_iface(void)
21 {
22 	return client_iface;
23 }
24 
test_coil_wr_rd(void)25 void test_coil_wr_rd(void)
26 {
27 	const uint8_t coil_qty = 16;
28 	uint8_t coil[3] = {0};
29 	int err;
30 
31 	for (uint16_t idx = 0; idx < coil_qty; idx++) {
32 		err = modbus_write_coil(client_iface, node, idx, true);
33 		zassert_equal(err, 0, "FC05 request failed");
34 	}
35 
36 	err = modbus_read_coils(client_iface, node, 0, coil, coil_qty);
37 	zassert_equal(err, 0, "FC01 request failed");
38 
39 	zassert_equal(coil[0], 0xff, "FC05 verify coil 0-7 failed");
40 	zassert_equal(coil[1], 0xff, "FC05 verify coil 8-15 failed");
41 
42 	for (uint16_t numof = 1; numof <= coil_qty; numof++) {
43 		err = modbus_write_coils(client_iface, node, 0, coil, numof);
44 		zassert_equal(err, 0, "FC15 request failed");
45 	}
46 
47 	coil[0] = 0xaa; coil[1] = 0xbb;
48 	err = modbus_write_coils(client_iface, node, 0, coil, coil_qty);
49 	zassert_equal(err, 0, "FC15 request failed");
50 
51 	err = modbus_read_coils(client_iface, node, 0, coil, coil_qty);
52 	zassert_equal(err, 0, "FC01 request failed");
53 
54 	zassert_equal(coil[0], 0xaa, "FC15 verify coil 0-7 failed");
55 	zassert_equal(coil[1], 0xbb, "FC15 verify coil 8-15 failed");
56 
57 	err = modbus_write_coil(client_iface, node, offset_oor, true);
58 	zassert_not_equal(err, 0, "FC05 out of range request not failed");
59 
60 	err = modbus_write_coils(client_iface, node, offset_oor, coil, coil_qty);
61 	zassert_not_equal(err, 0, "FC15 out of range request not failed");
62 }
63 
test_di_rd(void)64 void test_di_rd(void)
65 {
66 	const uint8_t di_qty = 16;
67 	uint8_t di[4] = {0};
68 	int err;
69 
70 	err = modbus_read_dinputs(client_iface, node, 0, di, di_qty);
71 	zassert_equal(err, 0, "FC02 request failed");
72 
73 	zassert_equal(di[0], 0xaa, "FC02 verify di 0-7 failed");
74 	zassert_equal(di[1], 0xbb, "FC02 verify di 8-15 failed");
75 
76 	err = modbus_read_dinputs(client_iface, node, 0, di, di_qty + 1);
77 	zassert_not_equal(err, 0, "FC02 out of range request not failed");
78 
79 	err = modbus_read_dinputs(client_iface, node, offset_oor, di, di_qty);
80 	zassert_not_equal(err, 0, "FC02 out of range request not failed");
81 }
82 
test_input_reg(void)83 void test_input_reg(void)
84 {
85 	uint16_t ir[8] = {0};
86 	int err;
87 
88 	err = modbus_write_holding_reg(client_iface, node, 0, 0xcafe);
89 	zassert_equal(err, 0, "FC06 write request for FC04 failed");
90 
91 	err = modbus_read_input_regs(client_iface, node, 0, ir, ARRAY_SIZE(ir));
92 	zassert_equal(err, 0, "FC04 request failed");
93 
94 	zassert_equal(ir[0], 0xcafe, "FC04 verify failed");
95 
96 	err = modbus_read_input_regs(client_iface,
97 				     node,
98 				     offset_oor,
99 				     ir,
100 				     ARRAY_SIZE(ir));
101 	zassert_not_equal(err, 0, "FC04 out of range request not failed");
102 }
103 
test_holding_reg(void)104 void test_holding_reg(void)
105 {
106 	uint16_t hr_wr[8] = {0, 2, 1, 3, 5, 4, 7, 6};
107 	uint16_t hr_rd[8] = {0};
108 	float fhr_wr[4] = {48.56470489501953125, 0.3, 0.2, 0.1};
109 	float fhr_rd[4] = {0.0};
110 	int err;
111 
112 	/* Test FC06 | FC03 */
113 	for (uint16_t idx = 0; idx < ARRAY_SIZE(hr_wr); idx++) {
114 		err = modbus_write_holding_reg(client_iface, node, idx, hr_wr[idx]);
115 		zassert_equal(err, 0, "FC06 write request failed");
116 	}
117 
118 	err = modbus_write_holding_reg(client_iface, node, offset_oor, 0xcafe);
119 	zassert_not_equal(err, 0, "FC06 out of range request not failed");
120 
121 	err = modbus_read_holding_regs(client_iface, node, 0,
122 				       hr_rd, ARRAY_SIZE(hr_rd));
123 	zassert_equal(err, 0, "FC03 read request failed");
124 
125 	LOG_HEXDUMP_DBG(hr_rd, sizeof(hr_rd), "FC06, hr_rd");
126 	zassert_equal(memcmp(hr_wr, hr_rd, sizeof(hr_wr)), 0,
127 		      "FC06 verify failed");
128 
129 	err = modbus_read_holding_regs(client_iface,
130 				       node,
131 				       offset_oor,
132 				       hr_rd,
133 				       ARRAY_SIZE(hr_rd));
134 	zassert_not_equal(err, 0, "FC03 out of range request not failed");
135 
136 	/* Test FC16 | FC03 */
137 	err = modbus_write_holding_regs(client_iface, node, 0,
138 					hr_wr, ARRAY_SIZE(hr_wr));
139 	zassert_equal(err, 0, "FC16 write request failed");
140 
141 	err = modbus_read_holding_regs(client_iface, node, 0,
142 				       hr_rd, ARRAY_SIZE(hr_rd));
143 	zassert_equal(err, 0, "FC03 read request failed");
144 
145 	LOG_HEXDUMP_DBG(hr_rd, sizeof(hr_rd), "FC16, hr_rd");
146 	zassert_equal(memcmp(hr_wr, hr_rd, sizeof(hr_wr)), 0,
147 		      "FC16 verify failed");
148 
149 	/* Test FC16 | FC03 */
150 	for (uint16_t idx = 0; idx < ARRAY_SIZE(fhr_wr); idx++) {
151 		err = modbus_write_holding_regs_fp(client_iface,
152 						 node,
153 						 fp_offset + idx * 2,
154 						 &fhr_wr[0], 1);
155 		zassert_equal(err, 0, "FC16 write request failed");
156 	}
157 
158 	err = modbus_write_holding_regs_fp(client_iface,
159 					   node,
160 					   fp_offset,
161 					   fhr_wr,
162 					   ARRAY_SIZE(fhr_wr));
163 	zassert_equal(err, 0, "FC16 FP request failed");
164 
165 	err = modbus_write_holding_regs_fp(client_iface,
166 					   node,
167 					   fp_offset_oor,
168 					   fhr_wr,
169 					   ARRAY_SIZE(fhr_wr));
170 	zassert_not_equal(err, 0, "FC16 FP out of range request not failed");
171 
172 	err = modbus_read_holding_regs_fp(client_iface,
173 					  node,
174 					  fp_offset_oor,
175 					  fhr_wr,
176 					  ARRAY_SIZE(fhr_wr));
177 	zassert_not_equal(err, 0, "FC16 FP out of range request not failed");
178 
179 	err = modbus_write_holding_regs(client_iface, node, fp_offset,
180 					hr_wr, ARRAY_SIZE(hr_wr) - 1);
181 	zassert_not_equal(err, 0, "FC16 write to FP address request not failed");
182 
183 	err = modbus_read_holding_regs(client_iface, node, fp_offset,
184 				       hr_rd, ARRAY_SIZE(hr_rd) - 1);
185 	zassert_not_equal(err, 0, "FC16 read from FP address request not failed");
186 
187 	err = modbus_read_holding_regs_fp(client_iface,
188 					  node,
189 					  fp_offset,
190 					  fhr_rd,
191 					  ARRAY_SIZE(fhr_rd));
192 	zassert_equal(err, 0, "FC03 read request failed");
193 
194 	LOG_HEXDUMP_DBG(fhr_rd, sizeof(fhr_rd), "FC16FP, fhr_rd");
195 	zassert_equal(memcmp(fhr_wr, fhr_rd, sizeof(fhr_wr)), 0,
196 		      "FC16FP verify failed");
197 }
198 
test_diagnostic(void)199 void test_diagnostic(void)
200 {
201 	uint16_t data = 0xcafe;
202 	int err;
203 
204 	for (uint16_t sf = 0x0A; sf < 0x0F; sf++) {
205 		err = modbus_request_diagnostic(client_iface, node, sf, 0, &data);
206 		zassert_equal(err, 0, "FC08:0x%04x request failed", sf);
207 	}
208 
209 	err = modbus_request_diagnostic(client_iface, node, 0xFF, 0, &data);
210 	zassert_not_equal(err, 0, "FC08 not supported request not failed");
211 }
212 
213 static struct modbus_iface_param client_param = {
214 	.mode = MODBUS_MODE_RTU,
215 	.rx_timeout = MB_TEST_RESPONSE_TO,
216 	.serial = {
217 		.baud = MB_TEST_BAUDRATE_LOW,
218 		.parity = UART_CFG_PARITY_ODD,
219 		.stop_bits_client = UART_CFG_STOP_BITS_1,
220 	},
221 };
222 
223 /*
224  * This test performed on hardware requires two UART controllers
225  * on the board (with RX/TX lines connected crosswise).
226  * The exact mapping is not required, we assume that both controllers
227  * have similar capabilities and use the instance with index 0
228  * as interface for the client.
229  */
230 #if DT_NODE_EXISTS(DT_INST(0, zephyr_modbus_serial))
231 static const char rtu_iface_name[] = {DEVICE_DT_NAME(DT_INST(0, zephyr_modbus_serial))};
232 #else
233 static const char rtu_iface_name[] = "";
234 #endif
235 
test_client_setup_low_none(void)236 void test_client_setup_low_none(void)
237 {
238 	int err;
239 
240 	client_iface = modbus_iface_get_by_name(rtu_iface_name);
241 	client_param.mode = MODBUS_MODE_RTU;
242 	client_param.serial.baud = MB_TEST_BAUDRATE_LOW;
243 	client_param.serial.parity = UART_CFG_PARITY_NONE;
244 	client_param.serial.stop_bits_client = UART_CFG_STOP_BITS_2;
245 
246 	err = modbus_init_client(client_iface, client_param);
247 	zassert_equal(err, 0, "Failed to configure RTU client");
248 }
249 
test_client_setup_low_odd(void)250 void test_client_setup_low_odd(void)
251 {
252 	int err;
253 
254 	client_iface = modbus_iface_get_by_name(rtu_iface_name);
255 	client_param.mode = MODBUS_MODE_RTU;
256 	client_param.serial.baud = MB_TEST_BAUDRATE_LOW;
257 	client_param.serial.parity = UART_CFG_PARITY_ODD;
258 	client_param.serial.stop_bits_client = UART_CFG_STOP_BITS_1;
259 
260 	err = modbus_init_client(client_iface, client_param);
261 	zassert_equal(err, 0, "Failed to configure RTU client");
262 }
263 
test_client_setup_high_even(void)264 void test_client_setup_high_even(void)
265 {
266 	int err;
267 
268 	client_iface = modbus_iface_get_by_name(rtu_iface_name);
269 	client_param.mode = MODBUS_MODE_RTU;
270 	client_param.serial.baud = MB_TEST_BAUDRATE_HIGH;
271 	client_param.serial.parity = UART_CFG_PARITY_EVEN;
272 	client_param.serial.stop_bits_client = UART_CFG_STOP_BITS_1;
273 
274 	err = modbus_init_client(client_iface, client_param);
275 	zassert_equal(err, 0, "Failed to configure RTU client");
276 }
277 
test_client_setup_ascii(void)278 void test_client_setup_ascii(void)
279 {
280 	int err;
281 
282 	client_iface = modbus_iface_get_by_name(rtu_iface_name);
283 	client_param.mode = MODBUS_MODE_ASCII;
284 	client_param.serial.baud = MB_TEST_BAUDRATE_HIGH;
285 	client_param.serial.parity = UART_CFG_PARITY_EVEN;
286 	client_param.serial.stop_bits_client = UART_CFG_STOP_BITS_1;
287 
288 	err = modbus_init_client(client_iface, client_param);
289 
290 	zassert_equal(err, 0, "Failed to configure RTU client");
291 }
292 
test_client_setup_raw(void)293 void test_client_setup_raw(void)
294 {
295 	char iface_name[] = "RAW_0";
296 	int err;
297 
298 	client_iface = modbus_iface_get_by_name(iface_name);
299 	client_param.mode = MODBUS_MODE_RAW;
300 	client_param.rawcb.raw_tx_cb = client_raw_cb;
301 	client_param.rawcb.user_data = NULL;
302 
303 	err = modbus_init_client(client_iface, client_param);
304 	zassert_equal(err, 0, "Failed to configure RAW client");
305 }
306 
test_client_disable(void)307 void test_client_disable(void)
308 {
309 	int err;
310 
311 	err = modbus_disable(client_iface);
312 	zassert_equal(err, 0, "Failed to disable RTU client");
313 }
314 
315 #else
316 
test_client_setup_low_none(void)317 void test_client_setup_low_none(void)
318 {
319 	ztest_test_skip();
320 }
321 
test_client_setup_low_odd(void)322 void test_client_setup_low_odd(void)
323 {
324 	ztest_test_skip();
325 }
326 
test_client_setup_high_even(void)327 void test_client_setup_high_even(void)
328 {
329 	ztest_test_skip();
330 }
331 
test_client_setup_ascii(void)332 void test_client_setup_ascii(void)
333 {
334 	ztest_test_skip();
335 }
336 
test_coil_wr_rd(void)337 void test_coil_wr_rd(void)
338 {
339 	ztest_test_skip();
340 }
341 
test_di_rd(void)342 void test_di_rd(void)
343 {
344 	ztest_test_skip();
345 }
346 
test_input_reg(void)347 void test_input_reg(void)
348 {
349 	ztest_test_skip();
350 }
351 
test_holding_reg(void)352 void test_holding_reg(void)
353 {
354 	ztest_test_skip();
355 }
356 
test_diagnostic(void)357 void test_diagnostic(void)
358 {
359 	ztest_test_skip();
360 }
361 
test_client_disable(void)362 void test_client_disable(void)
363 {
364 	ztest_test_skip();
365 }
366 
test_client_setup_raw(void)367 void test_client_setup_raw(void)
368 {
369 	ztest_test_skip();
370 }
371 #endif
372