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