1 #include <aos/kernel.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 
6 #include "mbmaster.h"
7 #include "py/builtin.h"
8 #include "py/obj.h"
9 #include "py/runtime.h"
10 #include "py/stackctrl.h"
11 #include "ulog/ulog.h"
12 
13 #define LOG_TAG "modbus"
14 
15 static mb_handler_t *mb_handler = NULL;
16 
mp_modbus_init(size_t n_args,const mp_obj_t * args)17 STATIC mp_obj_t mp_modbus_init(size_t n_args, const mp_obj_t *args)
18 {
19     if (mb_handler != NULL) {
20         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Device busy, deinit firstly"));
21         return mp_const_none;
22     }
23 
24     if (n_args == 0) {
25         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Args invalid"));
26         return mp_const_none;
27     }
28 
29     uint8_t port = 1;
30     uint32_t baud_rate = 9600;
31     mb_parity_t parity = MB_PAR_NONE;
32     uint32_t timeout = 200;
33 
34     if (n_args == 1) {
35         port = mp_obj_get_int(args[0]);
36     } else if (n_args == 2) {
37         port = mp_obj_get_int(args[0]);
38         baud_rate = mp_obj_get_int(args[1]);
39     } else if (n_args == 3) {
40         port = mp_obj_get_int(args[0]);
41         baud_rate = mp_obj_get_int(args[1]);
42         parity = mp_obj_get_int(args[2]);
43     } else if (n_args == 4) {
44         port = mp_obj_get_int(args[0]);
45         baud_rate = mp_obj_get_int(args[1]);
46         parity = mp_obj_get_int(args[2]);
47         int t = mp_obj_get_int(args[3]);
48         if (t < 0) {
49             timeout = AOS_WAIT_FOREVER;
50         } else {
51             timeout = t;
52         }
53     }
54 
55     if (parity != MB_PAR_NONE && parity != MB_PAR_ODD && parity != MB_PAR_EVEN) {
56         mp_raise_OSError(EINVAL);
57         return mp_const_none;
58     }
59 
60     mp_int_t status = mbmaster_rtu_init(&mb_handler, port, baud_rate, parity, timeout);
61     return MP_OBJ_NEW_SMALL_INT(-status);
62 }
63 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_modbus_init_obj, 0, 4, mp_modbus_init);
64 
mp_modbus_deinit(size_t n_args,const mp_obj_t * args)65 STATIC mp_obj_t mp_modbus_deinit(size_t n_args, const mp_obj_t *args)
66 {
67     if (mb_handler == NULL) {
68         mp_raise_OSError(EBADF);
69         return mp_const_none;
70     }
71 
72     mp_int_t status = mbmaster_rtu_uninit(mb_handler);
73     if (status == MB_SUCCESS) {
74         mb_handler = NULL;
75     }
76 
77     return MP_OBJ_NEW_SMALL_INT(-status);
78 }
79 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_modbus_deinit_obj, 0, mp_modbus_deinit);
80 
mp_writeHoldingRegister(size_t n_args,const mp_obj_t * args)81 STATIC mp_obj_t mp_writeHoldingRegister(size_t n_args, const mp_obj_t *args)
82 {
83     if (n_args != 4) {
84         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Should input 4 args"));
85         return mp_const_none;
86     }
87 
88     uint32_t timeout;
89     uint16_t resp_addr = 0;
90     uint8_t exception_code = 0;
91     uint16_t resp_data = 0;
92     uint8_t slave_addr = mp_obj_get_int(args[0]);
93     uint16_t register_addr = mp_obj_get_int(args[1]);
94     uint16_t register_value = mp_obj_get_int(args[2]);
95     int t = mp_obj_get_int(args[3]);
96     if (t < 0) {
97         timeout = AOS_WAIT_FOREVER;
98     } else {
99         timeout = t;
100     }
101 
102     mb_status_t status = mbmaster_write_single_register(mb_handler, slave_addr, register_addr, register_value,
103                                                         &resp_addr, &resp_data, &exception_code, timeout);
104     if (status == MB_SUCCESS && register_value == resp_data) {
105         LOGD(LOG_TAG, "write single register ok");
106     } else {
107         LOGE(LOG_TAG, "write single register error");
108     }
109 
110     mp_obj_t tuple[4] = {
111         MP_OBJ_NEW_SMALL_INT(-status),
112         mp_obj_new_int_from_uint(resp_addr),
113         mp_obj_new_int_from_uint(resp_data),
114         mp_obj_new_int_from_uint(exception_code),
115     };
116 
117     return mp_obj_new_tuple(4, tuple);
118 }
119 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_writeHoldingRegister_obj, 4, mp_writeHoldingRegister);
120 
mp_writeMultipleHoldingRegisters(size_t n_args,const mp_obj_t * args)121 STATIC mp_obj_t mp_writeMultipleHoldingRegisters(size_t n_args, const mp_obj_t *args)
122 {
123     if (n_args != 5) {
124         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Should input 5 args"));
125         return mp_const_none;
126     }
127 
128     uint16_t resp_addr = 0;
129     uint16_t resp_quantity = 0;
130     uint16_t exception_code = 0;
131     uint8_t slave_addr = mp_obj_get_int(args[0]);
132     uint16_t start_addr = mp_obj_get_int(args[1]);
133     uint16_t reg_quantity = mp_obj_get_int(args[2]);
134 
135     mp_buffer_info_t bufinfo;
136     mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ);
137     if (bufinfo.len < reg_quantity) {
138         mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("bytearray size should not smaller than "
139                                                         "twice of reg_quantity"));
140         return mp_const_none;
141     }
142 
143     uint32_t timeout;
144     int t = mp_obj_get_int(args[4]);
145     if (t < 0) {
146         timeout = AOS_WAIT_FOREVER;
147     } else {
148         timeout = t;
149     }
150 
151     mb_status_t status =
152         mbmaster_write_multiple_registers(mb_handler, slave_addr, start_addr, reg_quantity, bufinfo.buf, &resp_addr,
153                                           &resp_quantity, &exception_code, timeout);
154     if (status != MB_SUCCESS) {
155         LOGE(LOG_TAG, "WriteMultipleHoldingRegisters error");
156     }
157 
158     mp_obj_t tuple[4] = {
159         MP_OBJ_NEW_SMALL_INT(-status),
160         mp_obj_new_int_from_uint(resp_addr),
161         mp_obj_new_int_from_uint(resp_quantity),
162         mp_obj_new_int_from_uint(exception_code),
163     };
164 
165     return mp_obj_new_tuple(4, tuple);
166 }
167 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_writeMultipleHoldingRegisters_obj, 5, mp_writeMultipleHoldingRegisters);
168 
mp_writeCoil(size_t n_args,const mp_obj_t * args)169 STATIC mp_obj_t mp_writeCoil(size_t n_args, const mp_obj_t *args)
170 {
171     if (n_args != 4) {
172         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Should input 4 args"));
173         return mp_const_none;
174     }
175 
176     uint16_t resp_addr = 0;
177     uint16_t resp_value = 0;
178     uint8_t exception_code = 0;
179     uint8_t slave_addr = mp_obj_get_int(args[0]);
180     uint16_t coil_addr = mp_obj_get_int(args[1]);
181     uint16_t coil_value = mp_obj_get_int(args[2]);
182     uint32_t timeout;
183     int t = mp_obj_get_int(args[3]);
184     if (t < 0) {
185         timeout = AOS_WAIT_FOREVER;
186     } else {
187         timeout = t;
188     }
189 
190     mb_status_t status = mbmaster_write_single_coil(mb_handler, slave_addr, coil_addr, coil_value, &resp_addr,
191                                                     &resp_value, &exception_code, timeout);
192     if (status != MB_SUCCESS || coil_value != resp_value) {
193         LOGE(LOG_TAG, "write single register error");
194     }
195 
196     mp_obj_t tuple[4] = {
197         MP_OBJ_NEW_SMALL_INT(-status),
198         mp_obj_new_int_from_uint(resp_addr),
199         mp_obj_new_int_from_uint(resp_value),
200         mp_obj_new_int_from_uint(exception_code),
201     };
202 
203     return mp_obj_new_tuple(4, tuple);
204 }
205 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_writeCoil_obj, 4, mp_writeCoil);
206 
mp_writeMultipleCoils(size_t n_args,const mp_obj_t * args)207 STATIC mp_obj_t mp_writeMultipleCoils(size_t n_args, const mp_obj_t *args)
208 {
209     if (n_args != 5) {
210         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Should input 5 args"));
211         return mp_const_none;
212     }
213 
214     uint16_t resp_addr = 0;
215     uint16_t resp_quantity = 0;
216     uint16_t exception_code = 0;
217     uint8_t slave_addr = mp_obj_get_int(args[0]);
218     uint16_t start_addr = mp_obj_get_int(args[1]);
219     uint16_t reg_quantity = mp_obj_get_int(args[2]);
220 
221     mp_buffer_info_t bufinfo;
222     mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ);
223 
224     uint32_t timeout;
225     int t = mp_obj_get_int(args[4]);
226     if (t < 0) {
227         timeout = AOS_WAIT_FOREVER;
228     } else {
229         timeout = t;
230     }
231 
232     mb_status_t status = mbmaster_write_multiple_coils(mb_handler, slave_addr, start_addr, reg_quantity, bufinfo.buf,
233                                                        &resp_addr, &resp_quantity, &exception_code, timeout);
234     if (status != MB_SUCCESS) {
235         LOGE(LOG_TAG, "writeMultipleCoils error");
236     }
237 
238     mp_obj_t tuple[4] = {
239         MP_OBJ_NEW_SMALL_INT(-status),
240         mp_obj_new_int_from_uint(resp_addr),
241         mp_obj_new_int_from_uint(resp_quantity),
242         mp_obj_new_int_from_uint(exception_code),
243     };
244 
245     return mp_obj_new_tuple(4, tuple);
246 }
247 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_writeMultipleCoils_obj, 5, mp_writeMultipleCoils);
248 
mp_readHoldingRegisters(size_t n_args,const mp_obj_t * args)249 STATIC mp_obj_t mp_readHoldingRegisters(size_t n_args, const mp_obj_t *args)
250 {
251     if (n_args != 5) {
252         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Should input 5 args"));
253         return mp_const_none;
254     }
255 
256     uint8_t resp_quantity = 0;
257     uint8_t slave_addr = mp_obj_get_int(args[0]);
258     uint16_t start_addr = mp_obj_get_int(args[1]);
259     uint16_t reg_quantity = mp_obj_get_int(args[2]);
260 
261     mp_buffer_info_t bufinfo;
262     mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_WRITE);
263 
264     uint32_t timeout;
265     int t = mp_obj_get_int(args[4]);
266     if (t < 0) {
267         timeout = AOS_WAIT_FOREVER;
268     } else {
269         timeout = t;
270     }
271 
272     mb_status_t status = mbmaster_read_holding_registers(mb_handler, slave_addr, start_addr, reg_quantity, bufinfo.buf,
273                                                          &resp_quantity, timeout);
274     if (status != MB_SUCCESS) {
275         LOGE(LOG_TAG, "readHoldingRegisters error");
276     }
277 
278     mp_obj_t tuple[2] = {
279         MP_OBJ_NEW_SMALL_INT(-status),
280         mp_obj_new_int_from_uint(resp_quantity),
281     };
282 
283     return mp_obj_new_tuple(2, tuple);
284 }
285 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_readHoldingRegisters_obj, 5, mp_readHoldingRegisters);
286 
mp_readInputRegisters(size_t n_args,const mp_obj_t * args)287 STATIC mp_obj_t mp_readInputRegisters(size_t n_args, const mp_obj_t *args)
288 {
289     if (n_args != 5) {
290         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Should input 5 args"));
291         return mp_const_none;
292     }
293 
294     uint16_t resp_quantity = 0;
295     uint8_t slave_addr = mp_obj_get_int(args[0]);
296     uint16_t start_addr = mp_obj_get_int(args[1]);
297     uint16_t reg_quantity = mp_obj_get_int(args[2]);
298 
299     mp_buffer_info_t bufinfo;
300     mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_WRITE);
301 
302     uint32_t timeout;
303     int t = mp_obj_get_int(args[4]);
304     if (t < 0) {
305         timeout = AOS_WAIT_FOREVER;
306     } else {
307         timeout = t;
308     }
309 
310     mb_status_t status = mbmaster_read_input_registers(mb_handler, slave_addr, start_addr, reg_quantity, bufinfo.buf,
311                                                        &resp_quantity, timeout);
312     if (status != MB_SUCCESS) {
313         LOGE(LOG_TAG, "readInputRegisters error");
314     }
315 
316     mp_obj_t tuple[2] = {
317         MP_OBJ_NEW_SMALL_INT(-status),
318         mp_obj_new_int_from_uint(resp_quantity),
319     };
320 
321     return mp_obj_new_tuple(2, tuple);
322 }
323 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_readInputRegisters_obj, 5, mp_readInputRegisters);
324 
mp_readDiscreteInputs(size_t n_args,const mp_obj_t * args)325 STATIC mp_obj_t mp_readDiscreteInputs(size_t n_args, const mp_obj_t *args)
326 {
327     if (n_args != 5) {
328         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Should input 5 args"));
329         return mp_const_none;
330     }
331 
332     uint16_t resp_quantity = 0;
333     uint8_t slave_addr = mp_obj_get_int(args[0]);
334     uint16_t start_addr = mp_obj_get_int(args[1]);
335     uint16_t reg_quantity = mp_obj_get_int(args[2]);
336 
337     mp_buffer_info_t bufinfo;
338     mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_WRITE);
339 
340     uint32_t timeout;
341     int t = mp_obj_get_int(args[4]);
342     if (t < 0) {
343         timeout = AOS_WAIT_FOREVER;
344     } else {
345         timeout = t;
346     }
347 
348     mb_status_t status = mbmaster_read_discrete_inputs(mb_handler, slave_addr, start_addr, reg_quantity, bufinfo.buf,
349                                                        &resp_quantity, timeout);
350     if (status != MB_SUCCESS) {
351         LOGE(LOG_TAG, "readDiscreteInputs error");
352     }
353 
354     mp_obj_t tuple[2] = {
355         MP_OBJ_NEW_SMALL_INT(-status),
356         mp_obj_new_int_from_uint(resp_quantity),
357     };
358 
359     return mp_obj_new_tuple(2, tuple);
360 }
361 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_readDiscreteInputs_obj, 5, mp_readDiscreteInputs);
362 
mp_readCoils(size_t n_args,const mp_obj_t * args)363 STATIC mp_obj_t mp_readCoils(size_t n_args, const mp_obj_t *args)
364 {
365     if (n_args != 5) {
366         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Should input 5 args"));
367         return mp_const_none;
368     }
369 
370     uint16_t resp_quantity = 0;
371     uint8_t slave_addr = mp_obj_get_int(args[0]);
372     uint16_t start_addr = mp_obj_get_int(args[1]);
373     uint16_t reg_quantity = mp_obj_get_int(args[2]);
374 
375     mp_buffer_info_t bufinfo;
376     mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_WRITE);
377 
378     uint32_t timeout;
379     int t = mp_obj_get_int(args[4]);
380     if (t < 0) {
381         timeout = AOS_WAIT_FOREVER;
382     } else {
383         timeout = t;
384     }
385 
386     mb_status_t status =
387         mbmaster_read_coils(mb_handler, slave_addr, start_addr, reg_quantity, bufinfo.buf, &resp_quantity, timeout);
388     if (status != MB_SUCCESS) {
389         LOGE(LOG_TAG, "read_coils error");
390     }
391 
392     mp_obj_t tuple[2] = {
393         MP_OBJ_NEW_SMALL_INT(-status),
394         mp_obj_new_int_from_uint(resp_quantity),
395     };
396 
397     return mp_obj_new_tuple(2, tuple);
398 }
399 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_readCoils_obj, 5, mp_readCoils);
400 
mp_slave_framerecv(size_t n_args,const mp_obj_t * args)401 STATIC mp_obj_t mp_slave_framerecv(size_t n_args, const mp_obj_t *args)
402 {
403     if (mb_handler == NULL) {
404         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("slave not inited"));
405         return mp_const_none;
406     }
407 
408     // 1. recv frame from master
409     MP_THREAD_GIL_EXIT();
410     mb_status_t status = mb_handler->frame_recv(mb_handler);
411     MP_THREAD_GIL_ENTER();
412     if (status == MB_SUCCESS) {
413         // 2. disassemble frame
414         status = mb_handler->adu_disassemble(mb_handler);
415         if (status != MB_SUCCESS) {
416             LOGE(LOG_TAG, "Failed to disassemble frame");
417         }
418     }
419 
420     mp_obj_t tuple[2];
421     tuple[0] = MP_OBJ_NEW_SMALL_INT(-status);
422     tuple[1] =
423         status == MB_SUCCESS ? mp_obj_new_bytes(mb_handler->mb_frame_buff, mb_handler->mb_frame_length) : mp_const_none;
424 
425     return mp_obj_new_tuple(2, tuple);
426 }
427 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_slave_framerecv_obj, 0, mp_slave_framerecv);
428 
mp_slave_framesend(size_t n_args,const mp_obj_t * args)429 STATIC mp_obj_t mp_slave_framesend(size_t n_args, const mp_obj_t *args)
430 {
431     if (mb_handler == NULL) {
432         mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("slave not inited"));
433         return mp_const_none;
434     }
435 
436     uint16_t slave_addr = mp_obj_get_int(args[0]);
437     mp_buffer_info_t bufinfo;
438     mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
439 
440     uint32_t timeout;
441     int t = mp_obj_get_int(args[2]);
442     if (t < 0) {
443         timeout = AOS_WAIT_FOREVER;
444     } else {
445         timeout = t;
446     }
447 
448     mb_handler->slave_addr = slave_addr;
449     mb_handler->pdu_length = bufinfo.len;
450 
451     memcpy(&mb_handler->mb_frame_buff[ADU_SER_PDU_OFF], bufinfo.buf, bufinfo.len);
452 
453     mb_status_t status = mb_handler->adu_assemble(mb_handler);
454 
455     MP_THREAD_GIL_EXIT();
456     status = mb_handler->frame_send(mb_handler, timeout);
457     MP_THREAD_GIL_ENTER();
458     if (status != MB_SUCCESS) {
459         LOGE(LOG_TAG, "frame send failed");
460     }
461 
462     mp_obj_t tuple[2] = {
463         MP_OBJ_NEW_SMALL_INT(-status),
464         mp_obj_new_int_from_uint(mb_handler->mb_frame_length),
465     };
466 
467     return mp_obj_new_tuple(2, tuple);
468 }
469 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mp_slave_framesend_obj, 2, mp_slave_framesend);
470 
471 STATIC const mp_rom_map_elem_t modbus_module_globals_table[] = {
472     { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_modbus) },
473     { MP_OBJ_NEW_QSTR(MP_QSTR_init), MP_ROM_PTR(&mp_modbus_init_obj) },
474     { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mp_modbus_deinit_obj) },
475     { MP_OBJ_NEW_QSTR(MP_QSTR_writeHoldingRegister), MP_ROM_PTR(&mp_writeHoldingRegister_obj) },
476     { MP_OBJ_NEW_QSTR(MP_QSTR_writeMultipleHoldingRegisters), MP_ROM_PTR(&mp_writeMultipleHoldingRegisters_obj) },
477     { MP_OBJ_NEW_QSTR(MP_QSTR_writeCoil), MP_ROM_PTR(&mp_writeCoil_obj) },
478     { MP_OBJ_NEW_QSTR(MP_QSTR_writeMultipleCoils), MP_ROM_PTR(&mp_writeMultipleCoils_obj) },
479     { MP_OBJ_NEW_QSTR(MP_QSTR_readHoldingRegisters), MP_ROM_PTR(&mp_readHoldingRegisters_obj) },
480     { MP_OBJ_NEW_QSTR(MP_QSTR_readInputRegisters), MP_ROM_PTR(&mp_readInputRegisters_obj) },
481     { MP_OBJ_NEW_QSTR(MP_QSTR_readDiscreteInputs), MP_ROM_PTR(&mp_readDiscreteInputs_obj) },
482     { MP_OBJ_NEW_QSTR(MP_QSTR_readCoils), MP_ROM_PTR(&mp_readCoils_obj) },
483     { MP_OBJ_NEW_QSTR(MP_QSTR_recv), MP_ROM_PTR(&mp_slave_framerecv_obj) },
484     { MP_OBJ_NEW_QSTR(MP_QSTR_send), MP_ROM_PTR(&mp_slave_framesend_obj) },
485 
486     { MP_ROM_QSTR(MP_QSTR_PARITY_NONE), MP_ROM_INT(MB_PAR_NONE) },
487     { MP_ROM_QSTR(MP_QSTR_PARITY_ODD), MP_ROM_INT(MB_PAR_ODD) },
488     { MP_ROM_QSTR(MP_QSTR_PARITY_EVEN), MP_ROM_INT(MB_PAR_EVEN) },
489 };
490 
491 STATIC MP_DEFINE_CONST_DICT(modbus_module_globals, modbus_module_globals_table);
492 
493 const mp_obj_module_t modbus_module = {
494     .base = { &mp_type_module },
495     .globals = (mp_obj_dict_t *)&modbus_module_globals,
496 };
497