1 /*
2  * Copyright (c) 2020 PHYTEC Messtechnik GmbH
3  * Copyright (c) 2022 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 #include <zephyr/usb/usb_device.h>
13 
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(mbs_sample, LOG_LEVEL_INF);
16 
17 static uint16_t holding_reg[8];
18 static uint8_t coils_state;
19 
20 static const struct gpio_dt_spec led_dev[] = {
21 	GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios),
22 	GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios),
23 	GPIO_DT_SPEC_GET(DT_ALIAS(led2), gpios),
24 };
25 
coil_rd(uint16_t addr,bool * state)26 static int coil_rd(uint16_t addr, bool *state)
27 {
28 	if (addr >= ARRAY_SIZE(led_dev)) {
29 		return -ENOTSUP;
30 	}
31 
32 	if (coils_state & BIT(addr)) {
33 		*state = true;
34 	} else {
35 		*state = false;
36 	}
37 
38 	LOG_INF("Coil read, addr %u, %d", addr, (int)*state);
39 
40 	return 0;
41 }
42 
coil_wr(uint16_t addr,bool state)43 static int coil_wr(uint16_t addr, bool state)
44 {
45 	bool on;
46 
47 	if (addr >= ARRAY_SIZE(led_dev)) {
48 		return -ENOTSUP;
49 	}
50 
51 
52 	if (state == true) {
53 		coils_state |= BIT(addr);
54 		on = true;
55 	} else {
56 		coils_state &= ~BIT(addr);
57 		on = false;
58 	}
59 
60 	gpio_pin_set(led_dev[addr].port, led_dev[addr].pin, (int)on);
61 
62 	LOG_INF("Coil write, addr %u, %d", addr, (int)state);
63 
64 	return 0;
65 }
66 
holding_reg_rd(uint16_t addr,uint16_t * reg)67 static int holding_reg_rd(uint16_t addr, uint16_t *reg)
68 {
69 	if (addr >= ARRAY_SIZE(holding_reg)) {
70 		return -ENOTSUP;
71 	}
72 
73 	*reg = holding_reg[addr];
74 
75 	LOG_INF("Holding register read, addr %u", addr);
76 
77 	return 0;
78 }
79 
holding_reg_wr(uint16_t addr,uint16_t reg)80 static int holding_reg_wr(uint16_t addr, uint16_t reg)
81 {
82 	if (addr >= ARRAY_SIZE(holding_reg)) {
83 		return -ENOTSUP;
84 	}
85 
86 	holding_reg[addr] = reg;
87 
88 	LOG_INF("Holding register write, addr %u", addr);
89 
90 	return 0;
91 }
92 
93 static struct modbus_user_callbacks mbs_cbs = {
94 	.coil_rd = coil_rd,
95 	.coil_wr = coil_wr,
96 	.holding_reg_rd = holding_reg_rd,
97 	.holding_reg_wr = holding_reg_wr,
98 };
99 
100 const static struct modbus_iface_param server_param = {
101 	.mode = MODBUS_MODE_RTU,
102 	.server = {
103 		.user_cb = &mbs_cbs,
104 		.unit_id = 1,
105 	},
106 	.serial = {
107 		.baud = 19200,
108 		.parity = UART_CFG_PARITY_NONE,
109 	},
110 };
111 
112 #define MODBUS_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(zephyr_modbus_serial)
113 
init_modbus_server(void)114 static int init_modbus_server(void)
115 {
116 	const char iface_name[] = {DEVICE_DT_NAME(MODBUS_NODE)};
117 	int iface;
118 
119 	iface = modbus_iface_get_by_name(iface_name);
120 
121 	if (iface < 0) {
122 		LOG_ERR("Failed to get iface index for %s", iface_name);
123 		return iface;
124 	}
125 
126 	return modbus_init_server(iface, server_param);
127 }
128 
main(void)129 int main(void)
130 {
131 	int err;
132 
133 	for (int i = 0; i < ARRAY_SIZE(led_dev); i++) {
134 		if (!gpio_is_ready_dt(&led_dev[i])) {
135 			LOG_ERR("LED%u GPIO device not ready", i);
136 			return 0;
137 		}
138 
139 		err = gpio_pin_configure_dt(&led_dev[i], GPIO_OUTPUT_INACTIVE);
140 		if (err != 0) {
141 			LOG_ERR("Failed to configure LED%u pin", i);
142 			return 0;
143 		}
144 	}
145 
146 #if DT_NODE_HAS_COMPAT(DT_PARENT(MODBUS_NODE), zephyr_cdc_acm_uart)
147 	const struct device *const dev = DEVICE_DT_GET(DT_PARENT(MODBUS_NODE));
148 	uint32_t dtr = 0;
149 
150 	if (!device_is_ready(dev) || usb_enable(NULL)) {
151 		return 0;
152 	}
153 
154 	while (!dtr) {
155 		uart_line_ctrl_get(dev, UART_LINE_CTRL_DTR, &dtr);
156 		k_sleep(K_MSEC(100));
157 	}
158 
159 	LOG_INF("Client connected to server on %s", dev->name);
160 #endif
161 
162 	if (init_modbus_server()) {
163 		LOG_ERR("Modbus RTU server initialization failed");
164 	}
165 	return 0;
166 }
167