Dawn Framework 1.0
Universal data acquisition framework for embedded systems
rtu.cxx
1// dawn/src/proto/modbus/rtu.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/proto/modbus/rtu.hxx"
7
8#include "dawn/io/common.hxx"
9
10using namespace dawn;
11
12int CProtoModbusRtu::configureDesc(const CDescObject &desc)
13{
14 SObjectCfg::SObjectCfgItem *item = nullptr;
15 size_t offset = 0;
16
17 for (size_t i = 0; i < desc.getSize(); i++)
18 {
19 item = desc.objectCfgItemNext(offset);
20
22 {
23 DAWNERR("Unsupported Modbus RTU config 0x%08" PRIx32 "\n", item->cfgid.v);
24 return -EINVAL;
25 }
26
27 switch (item->cfgid.s.id)
28 {
30 {
31 for (size_t j = 0; j < item->cfgid.s.size;)
32 {
33 SProtoModbusIOBind *tmp = reinterpret_cast<SProtoModbusIOBind *>(item->data + j);
34
35 allocObject(tmp);
36
37 j += sizeof(SProtoModbusIOBind) / 4 + tmp->size;
38 }
39
40 break;
41 }
42
44 {
45 path = reinterpret_cast<const char *>(&item->data);
46 break;
47 }
48
50 {
51 baud = static_cast<uint32_t>(item->data[0]);
52 break;
53 }
54
55 default:
56 {
57 DAWNERR("Unsupported Modbus RTU config 0x%08" PRIx32 "\n", item->cfgid.v);
58 return -EINVAL;
59 }
60 }
61 }
62
63 return OK;
64}
65
66void CProtoModbusRtu::allocObject(SProtoModbusIOBind *alloc)
67{
68 for (size_t i = 0; i < alloc->size; i++)
69 {
70 DAWNINFO("allocate object 0x%" PRIx32 "\n", alloc->objid[i]);
71
72 // Allocate object in map
73
74 setObjectMapItem(alloc->objid[i], nullptr);
75 }
76
77 // Store config for later
78
79 valloc_push_back(alloc);
80};
81
82void CProtoModbusRtu::thread()
83{
84 DAWNINFO("start modbus thread\n");
85
86 /* Open the transport (nxmb_enable) here rather than in init(): a removable
87 * USB serial device (CDC/ACM) returns -ENOTCONN until the USB host has
88 * enumerated it, so wait for the host without blocking descriptor load.
89 */
90
91 for (;;)
92 {
93 int ret = nxmb_enable(mbhandle);
94 if (ret >= 0)
95 {
96 break;
97 }
98
99 if (ret != -ENOTCONN)
100 {
101 DAWNERR("ERROR: nxmb_enable failed: %d\n", ret);
102 workerThread().markThreadFinished();
103 return;
104 }
105
106 if (workerThread().shouldQuit())
107 {
108 workerThread().markThreadFinished();
109 return;
110 }
111
112 usleep(100000);
113 }
114
115 do
116 {
117 int ret;
118
119 /* Poll */
120
121 ret = nxmb_poll(mbhandle);
122 if (ret < 0 && ret != -ETIMEDOUT && ret != -EAGAIN)
123 {
124 DAWNERR("nxmb_poll error: %d\n", ret);
125 break;
126 }
127 }
128 while (!workerThread().shouldQuit());
129
130 // Mark thread quit done
131
132 workerThread().markThreadFinished();
133}
134
135int CProtoModbusRtu::modbusInitialize()
136{
137 struct nxmb_config_s config;
138 int ret;
139
140 memset(&config, 0, sizeof(config));
141 config.unit_id = saddr;
142 config.is_client = false;
143 config.mode = NXMB_MODE_RTU;
144
145 switch (parity)
146 {
147 case 0:
148 case 'N':
149 config.transport.serial.parity = NXMB_PAR_NONE;
150 break;
151 case 1:
152 case 'O':
153 config.transport.serial.parity = NXMB_PAR_ODD;
154 break;
155 case 2:
156 case 'E':
157 config.transport.serial.parity = NXMB_PAR_EVEN;
158 break;
159 default:
160 DAWNERR("ERROR: Invalid parity setting: %d\n", parity);
161 return -EINVAL;
162 }
163
164 config.transport.serial.devpath = path;
165 config.transport.serial.baudrate = baud;
166
167 ret = nxmb_create(&mbhandle, &config);
168 if (ret < 0)
169 {
170 DAWNERR("ERROR: nxmb_create failed: %d\n", ret);
171 return ret;
172 }
173
174 memset(&callbacks, 0, sizeof(callbacks));
175#ifdef CONFIG_DAWN_PROTO_MODBUS_COIL
176 callbacks.coil_cb = [](FAR uint8_t *buf,
177 uint16_t addr,
178 uint16_t ncoils,
179 enum nxmb_regmode_e mode,
180 FAR void *priv) -> int
181 { return static_cast<CProtoModbusRegs *>(priv)->coilsCb(buf, addr, ncoils, mode, priv); };
182#endif
183#ifdef CONFIG_DAWN_PROTO_MODBUS_DISCRETE
184 callbacks.discrete_cb =
185 [](FAR uint8_t *buf, uint16_t addr, uint16_t ndiscrete, FAR void *priv) -> int
186 { return static_cast<CProtoModbusRegs *>(priv)->discreteCb(buf, addr, ndiscrete, priv); };
187#endif
188#ifdef CONFIG_DAWN_PROTO_MODBUS_INPUT
189 callbacks.input_cb = [](FAR uint8_t *buf, uint16_t addr, uint16_t nregs, FAR void *priv) -> int
190 { return static_cast<CProtoModbusRegs *>(priv)->inputCb(buf, addr, nregs, priv); };
191#endif
192#ifdef CONFIG_DAWN_PROTO_MODBUS_HOLDING
193 callbacks.holding_cb = [](FAR uint8_t *buf,
194 uint16_t addr,
195 uint16_t nregs,
196 enum nxmb_regmode_e mode,
197 FAR void *priv) -> int
198 { return static_cast<CProtoModbusRegs *>(priv)->holdingCb(buf, addr, nregs, mode, priv); };
199#endif
200 callbacks.priv = static_cast<CProtoModbusRegs *>(this);
201
202 ret = nxmb_set_callbacks(mbhandle, &callbacks);
203 if (ret < 0)
204 {
205 DAWNERR("ERROR: nxmb_set_callbacks failed: %d\n", ret);
206 nxmb_destroy(mbhandle);
207 return ret;
208 }
209
210 /* nxmb_enable() opens the transport device; it is deferred to thread() so a
211 * not-yet-connected removable USB serial device (CDC/ACM) waits for the USB
212 * host without blocking the descriptor load.
213 */
214
215 return OK;
216}
217
218//***************************************************************************
219// Public Methods
220//***************************************************************************
221
222CProtoModbusRtu::~CProtoModbusRtu()
223{
224 deinit();
225}
226
228{
229 int ret;
230
231 // Configure object
232
233 ret = configureDesc(getDesc());
234 if (ret != OK)
235 {
236 DAWNERR("Modbus RTU configure failed (error %d)\n", ret);
237 return ret;
238 }
239
240 return OK;
241}
242
244{
245 int ret;
246
247 // NxModbus supports multi-instance, no singleton management needed
248
249 // Initialize modbus stack
250
251 ret = modbusInitialize();
252 if (ret < 0)
253 {
254 return ret;
255 }
256
257 // Create modbus registers after IO bindings are resolved
258
259 ret = createRegs();
260 if (ret < 0)
261 {
262 DAWNERR("failed to create registers %d\n", ret);
263 if (mbhandle != NULL)
264 {
265 nxmb_disable(mbhandle);
266 nxmb_destroy(mbhandle);
267 mbhandle = NULL;
268 }
269 return ret;
270 }
271
272 return OK;
273}
274
276{
277 destroyRegs();
278
279 // Release hardware resources
280
281 if (mbhandle != NULL)
282 {
283 nxmb_disable(mbhandle);
284 nxmb_destroy(mbhandle);
285 mbhandle = NULL;
286 }
287
288 return OK;
289}
290
292{
293 int ret;
294
295 // Start thread
296
297 ret = startWorkerThread([this]() { thread(); });
298 if (ret < 0)
299 {
300 DAWNERR("failed to start thread %d\n", ret);
301 return ret;
302 }
303
304 return OK;
305};
306
308{
309 /* Stop thread */
310
312
313 return OK;
314};
315
317{
318 return workerThreadRunning();
319}
void setObjectMapItem(SObjectId::ObjectId id, CObject *obj)
Set an item in the object map.
Definition bindable.cxx:23
Descriptor wrapper for individual object configuration.
size_t getSize() const
Get number of configuration items for this object.
SObjectCfg::SObjectCfgItem * objectCfgItemNext(size_t &offset) const
Get config item at current offset and advance past it.
CDescObject & getDesc()
Get descriptor object for this object.
Definition object.cxx:190
@ PROTO_CLASS_MODBUS_RTU
Modbus RTU (serial implementation).
Definition common.hxx:67
Modbus register management base class (shared RTU/TCP logic).
Definition regs.hxx:30
int configure()
Configure object from descriptor data.
Definition rtu.cxx:227
int init()
One-time initialize object after bindings are resolved.
Definition rtu.cxx:243
int doStop()
Stop implementation hook.
Definition rtu.cxx:307
@ PROTO_MODBUS_CFG_IOBIND
I/O object binding (register map).
Definition rtu.hxx:32
@ PROTO_MODBUS_CFG_PATH
Serial device path.
Definition rtu.hxx:33
@ PROTO_MODBUS_CFG_BAUD
Serial baud rate.
Definition rtu.hxx:34
int doStart()
Start implementation hook.
Definition rtu.cxx:291
int deinit()
De-initialize object.
Definition rtu.cxx:275
bool hasThread() const
Check if a background thread is active.
Definition rtu.cxx:316
bool workerThreadRunning() const
Check if the worker thread is running.
Definition thread.hxx:269
int stopWorkerThread()
Stop the worker thread.
Definition thread.hxx:258
int startWorkerThread(Func &&func)
Start the worker thread with a given function.
Definition thread.hxx:246
CThreadedObject & workerThread()
Get a reference to this thread controller.
Definition thread.hxx:280
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
Single configuration item within object.
ObjectCfgData_t data[]
Configuration data array (flexible, size from cfgid.s.size).
UObjectCfgId cfgid
Configuration ID header (type, class, id, size, rw, dtype).
ObjectCfgId v
Raw 32-bit ConfigID value (for storage, comparison).
Definition objectcfg.hxx:82
uint32_t cls
Object class (bits 21-29, max 511).
uint32_t id
Configuration identifier (bits 0-4, max 31).
Definition objectcfg.hxx:94
uint32_t size
Configuration data size in 32-bit words (bits 5-14, max 1023).
struct dawn::SObjectCfg::UObjectCfgId::@10 s
Bit-field structure for named member access.