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