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