Instrument Neutral Distributed Interface INDI 2.2.5
nanomodbus.c
Go to the documentation of this file.
1/*
2 nanoMODBUS - A compact MODBUS RTU/TCP C library for microcontrollers
3
4 MIT License
5
6 Copyright (c) 2024 Valerio De Benedetto (@debevv)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26
27#include "nanomodbus.h"
28
29#include <stdbool.h>
30#include <stdint.h>
31#include <string.h>
32
33#ifdef NMBS_DEBUG
34#include <stdio.h>
35#define NMBS_DEBUG_PRINT(...) printf(__VA_ARGS__)
36#else
37#define NMBS_DEBUG_PRINT(...) (void) (0)
38#endif
39
40
41static uint8_t get_1(nmbs_t* nmbs) {
42 uint8_t result = nmbs->msg.buf[nmbs->msg.buf_idx];
43 nmbs->msg.buf_idx++;
44 return result;
45}
46
47
48static void put_1(nmbs_t* nmbs, uint8_t data) {
49 nmbs->msg.buf[nmbs->msg.buf_idx] = data;
50 nmbs->msg.buf_idx++;
51}
52
53
54static void discard_1(nmbs_t* nmbs) {
55 nmbs->msg.buf_idx++;
56}
57
58
59#ifndef NMBS_SERVER_DISABLED
60static void discard_n(nmbs_t* nmbs, uint16_t n) {
61 nmbs->msg.buf_idx += n;
62}
63#endif
64
65
66static uint16_t get_2(nmbs_t* nmbs) {
67 uint16_t result =
68 ((uint16_t) nmbs->msg.buf[nmbs->msg.buf_idx]) << 8 | (uint16_t) nmbs->msg.buf[nmbs->msg.buf_idx + 1];
69 nmbs->msg.buf_idx += 2;
70 return result;
71}
72
73
74static void put_2(nmbs_t* nmbs, uint16_t data) {
75 nmbs->msg.buf[nmbs->msg.buf_idx] = (uint8_t) ((data >> 8) & 0xFFU);
76 nmbs->msg.buf[nmbs->msg.buf_idx + 1] = (uint8_t) data;
77 nmbs->msg.buf_idx += 2;
78}
79
80
81#ifndef NMBS_SERVER_DISABLED
82static void set_1(nmbs_t* nmbs, uint8_t data, uint8_t index) {
83 nmbs->msg.buf[index] = data;
84}
85
86
87static void set_2(nmbs_t* nmbs, uint16_t data, uint8_t index) {
88 nmbs->msg.buf[index] = (uint8_t) ((data >> 8) & 0xFFU);
89 nmbs->msg.buf[index + 1] = (uint8_t) data;
90}
91#endif
92
93
94static uint8_t* get_n(nmbs_t* nmbs, uint16_t n) {
95 uint8_t* msg_buf_ptr = nmbs->msg.buf + nmbs->msg.buf_idx;
96 nmbs->msg.buf_idx += n;
97 return msg_buf_ptr;
98}
99
100
101#ifndef NMBS_SERVER_DISABLED
102static void put_n(nmbs_t* nmbs, const uint8_t* data, uint8_t size) {
103 memcpy(&nmbs->msg.buf[nmbs->msg.buf_idx], data, size);
104 nmbs->msg.buf_idx += size;
105}
106
107
108static uint16_t* get_regs(nmbs_t* nmbs, uint16_t n) {
109 uint16_t* msg_buf_ptr = (uint16_t*) (nmbs->msg.buf + nmbs->msg.buf_idx);
110 nmbs->msg.buf_idx += n * 2;
111 while (n--) {
112 msg_buf_ptr[n] = (msg_buf_ptr[n] << 8) | ((msg_buf_ptr[n] >> 8) & 0xFF);
113 }
114 return msg_buf_ptr;
115}
116#endif
117
118
119#ifndef NMBS_CLIENT_DISABLED
120static void put_regs(nmbs_t* nmbs, const uint16_t* data, uint16_t n) {
121 uint16_t* msg_buf_ptr = (uint16_t*) (nmbs->msg.buf + nmbs->msg.buf_idx);
122 nmbs->msg.buf_idx += n * 2;
123 while (n--) {
124 msg_buf_ptr[n] = (data[n] << 8) | ((data[n] >> 8) & 0xFF);
125 }
126}
127#endif
128
129
130static void swap_regs(uint16_t* data, uint16_t n) {
131 while (n--) {
132 data[n] = (data[n] << 8) | ((data[n] >> 8) & 0xFF);
133 }
134}
135
136
137static void msg_buf_reset(nmbs_t* nmbs) {
138 nmbs->msg.buf_idx = 0;
139}
140
141
142static void msg_state_reset(nmbs_t* nmbs) {
143 msg_buf_reset(nmbs);
144 nmbs->msg.unit_id = 0;
145 nmbs->msg.fc = 0;
146 nmbs->msg.transaction_id = 0;
147 nmbs->msg.broadcast = false;
148 nmbs->msg.ignored = false;
149}
150
151
152#ifndef NMBS_CLIENT_DISABLED
153static void msg_state_req(nmbs_t* nmbs, uint8_t fc) {
154 if (nmbs->current_tid == UINT16_MAX)
155 nmbs->current_tid = 1;
156 else
157 nmbs->current_tid++;
158
159 // Flush the remaining data on the line before sending the request
160 nmbs->platform.read(nmbs->msg.buf, sizeof(nmbs->msg.buf), 0, nmbs->platform.arg);
161
162 msg_state_reset(nmbs);
163 nmbs->msg.unit_id = nmbs->dest_address_rtu;
164 nmbs->msg.fc = fc;
165 nmbs->msg.transaction_id = nmbs->current_tid;
166 if (nmbs->msg.unit_id == 0 && nmbs->platform.transport == NMBS_TRANSPORT_RTU)
167 nmbs->msg.broadcast = true;
168}
169#endif
170
171
172nmbs_error nmbs_create(nmbs_t* nmbs, const nmbs_platform_conf* platform_conf) {
173 if (!nmbs)
175
176 memset(nmbs, 0, sizeof(nmbs_t));
177
178 nmbs->byte_timeout_ms = -1;
179 nmbs->read_timeout_ms = -1;
180
181 if (!platform_conf)
183
184 if (platform_conf->transport != NMBS_TRANSPORT_RTU && platform_conf->transport != NMBS_TRANSPORT_TCP)
186
187 if (!platform_conf->read || !platform_conf->write)
189
190 nmbs->platform = *platform_conf;
191
192 return NMBS_ERROR_NONE;
193}
194
195
196void nmbs_set_read_timeout(nmbs_t* nmbs, int32_t timeout_ms) {
197 nmbs->read_timeout_ms = timeout_ms;
198}
199
200
201void nmbs_set_byte_timeout(nmbs_t* nmbs, int32_t timeout_ms) {
202 nmbs->byte_timeout_ms = timeout_ms;
203}
204
205
206void nmbs_set_destination_rtu_address(nmbs_t* nmbs, uint8_t address) {
207 nmbs->dest_address_rtu = address;
208}
209
210
211void nmbs_set_platform_arg(nmbs_t* nmbs, void* arg) {
212 nmbs->platform.arg = arg;
213}
214
215
216uint16_t nmbs_crc_calc(const uint8_t* data, uint32_t length) {
217 uint16_t crc = 0xFFFF;
218 for (uint32_t i = 0; i < length; i++) {
219 crc ^= (uint16_t) data[i];
220 for (int j = 8; j != 0; j--) {
221 if ((crc & 0x0001) != 0) {
222 crc >>= 1;
223 crc ^= 0xA001;
224 }
225 else
226 crc >>= 1;
227 }
228 }
229
230 return (uint16_t) (crc << 8) | (uint16_t) (crc >> 8);
231}
232
233
234static nmbs_error recv(nmbs_t* nmbs, uint16_t count) {
235 int32_t ret =
236 nmbs->platform.read(nmbs->msg.buf + nmbs->msg.buf_idx, count, nmbs->byte_timeout_ms, nmbs->platform.arg);
237
238 if (ret == count)
239 return NMBS_ERROR_NONE;
240
241 if (ret < count) {
242 if (ret < 0)
244
245 return NMBS_ERROR_TIMEOUT;
246 }
247
249}
250
251
252static nmbs_error send(nmbs_t* nmbs, uint16_t count) {
253 int32_t ret = nmbs->platform.write(nmbs->msg.buf, count, nmbs->byte_timeout_ms, nmbs->platform.arg);
254
255 if (ret == count)
256 return NMBS_ERROR_NONE;
257
258 if (ret < count) {
259 if (ret < 0)
261
262 return NMBS_ERROR_TIMEOUT;
263 }
264
266}
267
268
269static nmbs_error recv_msg_footer(nmbs_t* nmbs) {
270 NMBS_DEBUG_PRINT("\n");
271
273 uint16_t crc = nmbs_crc_calc(nmbs->msg.buf, nmbs->msg.buf_idx);
274
275 nmbs_error err = recv(nmbs, 2);
276 if (err != NMBS_ERROR_NONE)
277 return err;
278
279 uint16_t recv_crc = get_2(nmbs);
280
281 if (recv_crc != crc)
282 return NMBS_ERROR_CRC;
283 }
284
285 return NMBS_ERROR_NONE;
286}
287
288
289static nmbs_error recv_msg_header(nmbs_t* nmbs, bool* first_byte_received) {
290 // We wait for the read timeout here, just for the first message byte
291 int32_t old_byte_timeout = nmbs->byte_timeout_ms;
292 nmbs->byte_timeout_ms = nmbs->read_timeout_ms;
293
294 msg_state_reset(nmbs);
295
296 *first_byte_received = false;
297
299 nmbs_error err = recv(nmbs, 1);
300
301 nmbs->byte_timeout_ms = old_byte_timeout;
302
303 if (err != NMBS_ERROR_NONE)
304 return err;
305
306 *first_byte_received = true;
307
308 nmbs->msg.unit_id = get_1(nmbs);
309
310 err = recv(nmbs, 1);
311 if (err != NMBS_ERROR_NONE)
312 return err;
313
314 nmbs->msg.fc = get_1(nmbs);
315 }
316 else if (nmbs->platform.transport == NMBS_TRANSPORT_TCP) {
317 nmbs_error err = recv(nmbs, 1);
318
319 nmbs->byte_timeout_ms = old_byte_timeout;
320
321 if (err != NMBS_ERROR_NONE)
322 return err;
323
324 *first_byte_received = true;
325
326 // Advance buf_idx
327 discard_1(nmbs);
328
329 err = recv(nmbs, 7);
330 if (err != NMBS_ERROR_NONE)
331 return err;
332
333 // Starting over
334 msg_buf_reset(nmbs);
335
336 nmbs->msg.transaction_id = get_2(nmbs);
337 uint16_t protocol_id = get_2(nmbs);
338 uint16_t length = get_2(nmbs); // We should actually check the length of the request against this value
339 nmbs->msg.unit_id = get_1(nmbs);
340 nmbs->msg.fc = get_1(nmbs);
341
342 if (protocol_id != 0)
344
345 if (length > 255)
347 }
348
349 return NMBS_ERROR_NONE;
350}
351
352
353static void put_msg_header(nmbs_t* nmbs, uint16_t data_length) {
354 msg_buf_reset(nmbs);
355
357 put_1(nmbs, nmbs->msg.unit_id);
358 }
359 else if (nmbs->platform.transport == NMBS_TRANSPORT_TCP) {
360 put_2(nmbs, nmbs->msg.transaction_id);
361 put_2(nmbs, 0);
362 put_2(nmbs, (uint16_t) (1 + 1 + data_length));
363 put_1(nmbs, nmbs->msg.unit_id);
364 }
365
366 put_1(nmbs, nmbs->msg.fc);
367}
368
369
370#ifndef NMBS_SERVER_DISABLED
371static void set_msg_header_size(nmbs_t* nmbs, uint16_t data_length) {
373 data_length += 2;
374 set_2(nmbs, data_length, 4);
375 }
376}
377#endif
378
379
380static nmbs_error send_msg(nmbs_t* nmbs) {
381 NMBS_DEBUG_PRINT("\n");
382
384 uint16_t crc = nmbs_crc_calc(nmbs->msg.buf, nmbs->msg.buf_idx);
385 put_2(nmbs, crc);
386 }
387
388 nmbs_error err = send(nmbs, nmbs->msg.buf_idx);
389
390 return err;
391}
392
393
394#ifndef NMBS_SERVER_DISABLED
395static nmbs_error recv_req_header(nmbs_t* nmbs, bool* first_byte_received) {
396 nmbs_error err = recv_msg_header(nmbs, first_byte_received);
397 if (err != NMBS_ERROR_NONE)
398 return err;
399
401 // Check if request is for us
402 if (nmbs->msg.unit_id == NMBS_BROADCAST_ADDRESS)
403 nmbs->msg.broadcast = true;
404 else if (nmbs->msg.unit_id != nmbs->address_rtu)
405 nmbs->msg.ignored = true;
406 else
407 nmbs->msg.ignored = false;
408 }
409
410 return NMBS_ERROR_NONE;
411}
412
413
414static void put_res_header(nmbs_t* nmbs, uint16_t data_length) {
415 put_msg_header(nmbs, data_length);
416 NMBS_DEBUG_PRINT("%d NMBS res -> address_rtu %d\tfc %d\t", nmbs->address_rtu, nmbs->address_rtu, nmbs->msg.fc);
417}
418
419
420static nmbs_error send_exception_msg(nmbs_t* nmbs, uint8_t exception) {
421 nmbs->msg.fc += 0x80;
422 put_msg_header(nmbs, 1);
423 put_1(nmbs, exception);
424
425 NMBS_DEBUG_PRINT("%d NMBS res -> address_rtu %d\texception %d", nmbs->address_rtu, nmbs->address_rtu, exception);
426
427 return send_msg(nmbs);
428}
429#endif
430
431
432static nmbs_error recv_res_header(nmbs_t* nmbs) {
433 uint16_t req_transaction_id = nmbs->msg.transaction_id;
434 uint8_t req_unit_id = nmbs->msg.unit_id;
435 uint8_t req_fc = nmbs->msg.fc;
436
437 bool first_byte_received = false;
438 nmbs_error err = recv_msg_header(nmbs, &first_byte_received);
439 if (err != NMBS_ERROR_NONE)
440 return err;
441
443 if (nmbs->msg.transaction_id != req_transaction_id)
445 }
446
447 if (nmbs->platform.transport == NMBS_TRANSPORT_RTU && nmbs->msg.unit_id != req_unit_id)
449
450 if (nmbs->msg.fc != req_fc) {
451 if (nmbs->msg.fc - 0x80 == req_fc) {
452 err = recv(nmbs, 1);
453 if (err != NMBS_ERROR_NONE)
454 return err;
455
456 uint8_t exception = get_1(nmbs);
457 err = recv_msg_footer(nmbs);
458 if (err != NMBS_ERROR_NONE)
459 return err;
460
461 if (exception < 1 || exception > 4)
463
464 NMBS_DEBUG_PRINT("%d NMBS res <- address_rtu %d\texception %d\n", nmbs->address_rtu, nmbs->msg.unit_id,
465 exception);
466 return (nmbs_error) exception;
467 }
468
470 }
471
472 NMBS_DEBUG_PRINT("%d NMBS res <- address_rtu %d\tfc %d\t", nmbs->address_rtu, nmbs->msg.unit_id, nmbs->msg.fc);
473
474 return NMBS_ERROR_NONE;
475}
476
477
478#ifndef NMBS_CLIENT_DISABLED
479static void put_req_header(nmbs_t* nmbs, uint16_t data_length) {
480 put_msg_header(nmbs, data_length);
481#ifdef NMBS_DEBUG
482 printf("%d ", nmbs->address_rtu);
483 printf("NMBS req -> ");
485 if (nmbs->msg.broadcast)
486 printf("broadcast\t");
487 else
488 printf("address_rtu %d\t", nmbs->dest_address_rtu);
489 }
490
491 printf("fc %d\t", nmbs->msg.fc);
492#endif
493}
494#endif
495
496
497static nmbs_error recv_read_discrete_res(nmbs_t* nmbs, nmbs_bitfield values) {
498 nmbs_error err = recv_res_header(nmbs);
499 if (err != NMBS_ERROR_NONE)
500 return err;
501
502 err = recv(nmbs, 1);
503 if (err != NMBS_ERROR_NONE)
504 return err;
505
506 uint8_t coils_bytes = get_1(nmbs);
507 NMBS_DEBUG_PRINT("b %d\t", coils_bytes);
508
509 if (coils_bytes > 250) {
511 }
512
513 err = recv(nmbs, coils_bytes);
514 if (err != NMBS_ERROR_NONE)
515 return err;
516
517 NMBS_DEBUG_PRINT("coils ");
518 for (int i = 0; i < coils_bytes; i++) {
519 uint8_t coil = get_1(nmbs);
520 if (values)
521 values[i] = coil;
522 NMBS_DEBUG_PRINT("%d ", coil);
523 }
524
525 err = recv_msg_footer(nmbs);
526 if (err != NMBS_ERROR_NONE)
527 return err;
528
529 return NMBS_ERROR_NONE;
530}
531
532
533static nmbs_error recv_read_registers_res(nmbs_t* nmbs, uint16_t quantity, uint16_t* registers) {
534 nmbs_error err = recv_res_header(nmbs);
535 if (err != NMBS_ERROR_NONE)
536 return err;
537
538 err = recv(nmbs, 1);
539 if (err != NMBS_ERROR_NONE)
540 return err;
541
542 uint8_t registers_bytes = get_1(nmbs);
543 NMBS_DEBUG_PRINT("b %d\t", registers_bytes);
544
545 if (registers_bytes > 250)
547
548 err = recv(nmbs, registers_bytes);
549 if (err != NMBS_ERROR_NONE)
550 return err;
551
552 NMBS_DEBUG_PRINT("regs ");
553 for (int i = 0; i < registers_bytes / 2; i++) {
554 uint16_t reg = get_2(nmbs);
555 if (registers)
556 registers[i] = reg;
557 NMBS_DEBUG_PRINT("%d ", reg);
558 }
559
560 err = recv_msg_footer(nmbs);
561 if (err != NMBS_ERROR_NONE)
562 return err;
563
564 if (registers_bytes != quantity * 2)
566
567 return NMBS_ERROR_NONE;
568}
569
570
571nmbs_error recv_write_single_coil_res(nmbs_t* nmbs, uint16_t address, uint16_t value_req) {
572 nmbs_error err = recv_res_header(nmbs);
573 if (err != NMBS_ERROR_NONE)
574 return err;
575
576 err = recv(nmbs, 4);
577 if (err != NMBS_ERROR_NONE)
578 return err;
579
580 uint16_t address_res = get_2(nmbs);
581 uint16_t value_res = get_2(nmbs);
582
583 NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value_res);
584
585 err = recv_msg_footer(nmbs);
586 if (err != NMBS_ERROR_NONE)
587 return err;
588
589 if (address_res != address)
591
592 if (value_res != value_req)
594
595 return NMBS_ERROR_NONE;
596}
597
598
599nmbs_error recv_write_single_register_res(nmbs_t* nmbs, uint16_t address, uint16_t value_req) {
600 nmbs_error err = recv_res_header(nmbs);
601 if (err != NMBS_ERROR_NONE)
602 return err;
603
604 err = recv(nmbs, 4);
605 if (err != NMBS_ERROR_NONE)
606 return err;
607
608 uint16_t address_res = get_2(nmbs);
609 uint16_t value_res = get_2(nmbs);
610 NMBS_DEBUG_PRINT("a %d\tvalue %d ", address, value_res);
611
612 err = recv_msg_footer(nmbs);
613 if (err != NMBS_ERROR_NONE)
614 return err;
615
616 if (address_res != address)
618
619 if (value_res != value_req)
621
622 return NMBS_ERROR_NONE;
623}
624
625
626nmbs_error recv_write_multiple_coils_res(nmbs_t* nmbs, uint16_t address, uint16_t quantity) {
627 nmbs_error err = recv_res_header(nmbs);
628 if (err != NMBS_ERROR_NONE)
629 return err;
630
631 err = recv(nmbs, 4);
632 if (err != NMBS_ERROR_NONE)
633 return err;
634
635 uint16_t address_res = get_2(nmbs);
636 uint16_t quantity_res = get_2(nmbs);
637 NMBS_DEBUG_PRINT("a %d\tq %d", address_res, quantity_res);
638
639 err = recv_msg_footer(nmbs);
640 if (err != NMBS_ERROR_NONE)
641 return err;
642
643 if (address_res != address)
645
646 if (quantity_res != quantity)
648
649 return NMBS_ERROR_NONE;
650}
651
652
653nmbs_error recv_write_multiple_registers_res(nmbs_t* nmbs, uint16_t address, uint16_t quantity) {
654 nmbs_error err = recv_res_header(nmbs);
655 if (err != NMBS_ERROR_NONE)
656 return err;
657
658 err = recv(nmbs, 4);
659 if (err != NMBS_ERROR_NONE)
660 return err;
661
662 uint16_t address_res = get_2(nmbs);
663 uint16_t quantity_res = get_2(nmbs);
664 NMBS_DEBUG_PRINT("a %d\tq %d", address_res, quantity_res);
665
666 err = recv_msg_footer(nmbs);
667 if (err != NMBS_ERROR_NONE)
668 return err;
669
670 if (address_res != address)
672
673 if (quantity_res != quantity)
675
676 return NMBS_ERROR_NONE;
677}
678
679
680nmbs_error recv_read_file_record_res(nmbs_t* nmbs, uint16_t* registers, uint16_t count) {
681 nmbs_error err = recv_res_header(nmbs);
682 if (err != NMBS_ERROR_NONE)
683 return err;
684
685 err = recv(nmbs, 1);
686 if (err != NMBS_ERROR_NONE)
687 return err;
688
689 uint8_t response_size = get_1(nmbs);
690 if (response_size > 250) {
692 }
693
694 err = recv(nmbs, response_size);
695 if (err != NMBS_ERROR_NONE)
696 return err;
697
698 uint8_t subreq_data_size = get_1(nmbs) - 1;
699 uint8_t subreq_reference_type = get_1(nmbs);
700 uint16_t* subreq_record_data = (uint16_t*) get_n(nmbs, subreq_data_size);
701
702 err = recv_msg_footer(nmbs);
703 if (err != NMBS_ERROR_NONE)
704 return err;
705
706 if (registers) {
707 if (subreq_reference_type != 6)
709
710 if (count != (subreq_data_size / 2))
712
713 swap_regs(subreq_record_data, subreq_data_size / 2);
714 memcpy(registers, subreq_record_data, subreq_data_size);
715 }
716
717 return NMBS_ERROR_NONE;
718}
719
720
721nmbs_error recv_write_file_record_res(nmbs_t* nmbs, uint16_t file_number, uint16_t record_number,
722 const uint16_t* registers, uint16_t count) {
723 nmbs_error err = recv_res_header(nmbs);
724 if (err != NMBS_ERROR_NONE)
725 return err;
726
727 err = recv(nmbs, 1);
728 if (err != NMBS_ERROR_NONE)
729 return err;
730
731 uint8_t response_size = get_1(nmbs);
732 if (response_size > 251)
734
735 err = recv(nmbs, response_size);
736 if (err != NMBS_ERROR_NONE)
737 return err;
738
739 uint8_t subreq_reference_type = get_1(nmbs);
740 uint16_t subreq_file_number = get_2(nmbs);
741 uint16_t subreq_record_number = get_2(nmbs);
742 uint16_t subreq_record_length = get_2(nmbs);
743 NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fwrite ", subreq_file_number, subreq_record_number, subreq_record_length);
744
745 uint16_t subreq_data_size = subreq_record_length * 2;
746 uint16_t* subreq_record_data = (uint16_t*) get_n(nmbs, subreq_data_size);
747
748 err = recv_msg_footer(nmbs);
749 if (err != NMBS_ERROR_NONE)
750 return err;
751
752 if (registers) {
753 if (subreq_reference_type != 6)
755
756 if (subreq_file_number != file_number)
758
759 if (subreq_record_number != record_number)
761
762 if (subreq_record_length != count)
764
765 swap_regs(subreq_record_data, subreq_record_length);
766 if (memcmp(registers, subreq_record_data, subreq_data_size) != 0)
768 }
769
770 return NMBS_ERROR_NONE;
771}
772
773nmbs_error recv_read_device_identification_res(nmbs_t* nmbs, uint8_t buffers_count, char** buffers_out,
774 uint8_t buffers_length, const uint8_t* order, uint8_t* ids_out,
775 uint8_t* next_object_id_out, uint8_t* objects_count_out) {
776 nmbs_error err = recv_res_header(nmbs);
777 if (err != NMBS_ERROR_NONE)
778 return err;
779
780 err = recv(nmbs, 6);
781 if (err != NMBS_ERROR_NONE)
782 return err;
783
784 uint8_t mei_type = get_1(nmbs);
785 if (mei_type != 0x0E)
787
788 uint8_t read_device_id_code = get_1(nmbs);
789 if (read_device_id_code < 1 || read_device_id_code > 4)
791
792 uint8_t conformity_level = get_1(nmbs);
793 if (conformity_level < 1 || (conformity_level > 3 && conformity_level < 0x81) || conformity_level > 0x83)
795
796 uint8_t more_follows = get_1(nmbs);
797 if (more_follows != 0 && more_follows != 0xFF)
799
800 uint8_t next_object_id = get_1(nmbs);
801
802 uint8_t objects_count = get_1(nmbs);
803 if (objects_count_out)
804 *objects_count_out = objects_count;
805
806 if (buffers_count == 0) {
807 buffers_out = NULL;
808 }
809 else if (objects_count > buffers_count)
811
812 if (more_follows == 0)
813 next_object_id = 0x7F; // This value is reserved in the spec, we use it to signal stream is finished
814
815 if (next_object_id_out)
816 *next_object_id_out = next_object_id;
817
818 uint8_t res_size_left = 253 - 7;
819 for (int i = 0; i < objects_count; i++) {
820 err = recv(nmbs, 2);
821 if (err != NMBS_ERROR_NONE)
822 return err;
823
824 uint8_t object_id = get_1(nmbs);
825 uint8_t object_length = get_1(nmbs);
826 res_size_left -= 2;
827
828 if (object_length > res_size_left)
830
831 err = recv(nmbs, object_length);
832 if (err != NMBS_ERROR_NONE)
833 return err;
834
835 const char* str = (const char*) get_n(nmbs, object_length);
836
837 if (ids_out)
838 ids_out[i] = object_id;
839
840 uint8_t buf_index = i;
841 if (order)
842 buf_index = order[object_id];
843 if (buffers_out) {
844 strncpy(buffers_out[buf_index], str, buffers_length);
845 buffers_out[buf_index][object_length] = 0;
846 }
847 }
848
849 return recv_msg_footer(nmbs);
850}
851
852
853#ifndef NMBS_SERVER_DISABLED
854#if !defined(NMBS_SERVER_READ_COILS_DISABLED) || !defined(NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED)
855static nmbs_error handle_read_discrete(nmbs_t* nmbs,
856 nmbs_error (*callback)(uint16_t, uint16_t, nmbs_bitfield, uint8_t, void*)) {
857 nmbs_error err = recv(nmbs, 4);
858 if (err != NMBS_ERROR_NONE)
859 return err;
860
861 uint16_t address = get_2(nmbs);
862 uint16_t quantity = get_2(nmbs);
863
864 NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
865
866 err = recv_msg_footer(nmbs);
867 if (err != NMBS_ERROR_NONE)
868 return err;
869
870 if (!nmbs->msg.ignored) {
871 if (quantity < 1 || quantity > 2000)
872 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
873
874 if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
875 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
876
877 if (callback) {
878 nmbs_bitfield bitfield = {0};
879 err = callback(address, quantity, bitfield, nmbs->msg.unit_id, nmbs->callbacks.arg);
880 if (err != NMBS_ERROR_NONE) {
882 return send_exception_msg(nmbs, err);
883
884 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
885 }
886
887 if (!nmbs->msg.broadcast) {
888 uint8_t discrete_bytes = (quantity + 7) / 8;
889 put_res_header(nmbs, 1 + discrete_bytes);
890
891 put_1(nmbs, discrete_bytes);
892
893 NMBS_DEBUG_PRINT("b %d\t", discrete_bytes);
894
895 NMBS_DEBUG_PRINT("coils ");
896 for (int i = 0; i < discrete_bytes; i++) {
897 put_1(nmbs, bitfield[i]);
898 NMBS_DEBUG_PRINT("%d ", bitfield[i]);
899 }
900
901 err = send_msg(nmbs);
902 if (err != NMBS_ERROR_NONE)
903 return err;
904 }
905 }
906 else {
907 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
908 }
909 }
910 else {
911 return recv_read_discrete_res(nmbs, NULL);
912 }
913
914 return NMBS_ERROR_NONE;
915}
916#endif
917
918
919#if !defined(NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED) || !defined(NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED)
920static nmbs_error handle_read_registers(nmbs_t* nmbs,
921 nmbs_error (*callback)(uint16_t, uint16_t, uint16_t*, uint8_t, void*)) {
922 nmbs_error err = recv(nmbs, 4);
923 if (err != NMBS_ERROR_NONE)
924 return err;
925
926 uint16_t address = get_2(nmbs);
927 uint16_t quantity = get_2(nmbs);
928
929 NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
930
931 err = recv_msg_footer(nmbs);
932 if (err != NMBS_ERROR_NONE)
933 return err;
934
935 if (!nmbs->msg.ignored) {
936 if (quantity < 1 || quantity > 125)
937 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
938
939 if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
940 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
941
942 if (callback) {
943 uint16_t regs[125] = {0};
944 err = callback(address, quantity, regs, nmbs->msg.unit_id, nmbs->callbacks.arg);
945 if (err != NMBS_ERROR_NONE) {
947 return send_exception_msg(nmbs, err);
948
949 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
950 }
951
952 // TODO check all these read request broadcast use cases
953 if (!nmbs->msg.broadcast) {
954 uint8_t regs_bytes = quantity * 2;
955 put_res_header(nmbs, 1 + regs_bytes);
956
957 put_1(nmbs, regs_bytes);
958
959 NMBS_DEBUG_PRINT("b %d\t", regs_bytes);
960
961 NMBS_DEBUG_PRINT("regs ");
962 for (int i = 0; i < quantity; i++) {
963 put_2(nmbs, regs[i]);
964 NMBS_DEBUG_PRINT("%d ", regs[i]);
965 }
966
967 err = send_msg(nmbs);
968 if (err != NMBS_ERROR_NONE)
969 return err;
970 }
971 }
972 else {
973 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
974 }
975 }
976 else {
977 return recv_read_registers_res(nmbs, quantity, NULL);
978 }
979
980 return NMBS_ERROR_NONE;
981}
982#endif
983
984
985#ifndef NMBS_SERVER_READ_COILS_DISABLED
986static nmbs_error handle_read_coils(nmbs_t* nmbs) {
987 return handle_read_discrete(nmbs, nmbs->callbacks.read_coils);
988}
989#endif
990
991
992#ifndef NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED
993static nmbs_error handle_read_discrete_inputs(nmbs_t* nmbs) {
994 return handle_read_discrete(nmbs, nmbs->callbacks.read_discrete_inputs);
995}
996#endif
997
998
999#ifndef NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED
1000static nmbs_error handle_read_holding_registers(nmbs_t* nmbs) {
1001 return handle_read_registers(nmbs, nmbs->callbacks.read_holding_registers);
1002}
1003#endif
1004
1005
1006#ifndef NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED
1007static nmbs_error handle_read_input_registers(nmbs_t* nmbs) {
1008 return handle_read_registers(nmbs, nmbs->callbacks.read_input_registers);
1009}
1010#endif
1011
1012
1013#ifndef NMBS_SERVER_WRITE_SINGLE_COIL_DISABLED
1014static nmbs_error handle_write_single_coil(nmbs_t* nmbs) {
1015 nmbs_error err = recv(nmbs, 4);
1016 if (err != NMBS_ERROR_NONE)
1017 return err;
1018
1019 uint16_t address = get_2(nmbs);
1020 uint16_t value = get_2(nmbs);
1021
1022 NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
1023
1024 err = recv_msg_footer(nmbs);
1025 if (err != NMBS_ERROR_NONE)
1026 return err;
1027
1028 if (!nmbs->msg.ignored) {
1029 if (nmbs->callbacks.write_single_coil) {
1030 if (value != 0 && value != 0xFF00)
1031 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1032
1033 err = nmbs->callbacks.write_single_coil(address, value == 0 ? false : true, nmbs->msg.unit_id,
1034 nmbs->callbacks.arg);
1035 if (err != NMBS_ERROR_NONE) {
1036 if (nmbs_error_is_exception(err))
1037 return send_exception_msg(nmbs, err);
1038
1039 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1040 }
1041
1042 if (!nmbs->msg.broadcast) {
1043 put_res_header(nmbs, 4);
1044
1045 put_2(nmbs, address);
1046 put_2(nmbs, value);
1047 NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
1048
1049 err = send_msg(nmbs);
1050 if (err != NMBS_ERROR_NONE)
1051 return err;
1052 }
1053 }
1054 else {
1055 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1056 }
1057 }
1058 else {
1059 return recv_write_single_coil_res(nmbs, address, value);
1060 }
1061
1062 return NMBS_ERROR_NONE;
1063}
1064#endif
1065
1066
1067#ifndef NMBS_SERVER_WRITE_SINGLE_REGISTER_DISABLED
1068static nmbs_error handle_write_single_register(nmbs_t* nmbs) {
1069 nmbs_error err = recv(nmbs, 4);
1070 if (err != NMBS_ERROR_NONE)
1071 return err;
1072
1073 uint16_t address = get_2(nmbs);
1074 uint16_t value = get_2(nmbs);
1075
1076 NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
1077
1078 err = recv_msg_footer(nmbs);
1079 if (err != NMBS_ERROR_NONE)
1080 return err;
1081
1082 if (!nmbs->msg.ignored) {
1083 if (nmbs->callbacks.write_single_register) {
1084 err = nmbs->callbacks.write_single_register(address, value, nmbs->msg.unit_id, nmbs->callbacks.arg);
1085 if (err != NMBS_ERROR_NONE) {
1086 if (nmbs_error_is_exception(err))
1087 return send_exception_msg(nmbs, err);
1088
1089 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1090 }
1091
1092 if (!nmbs->msg.broadcast) {
1093 put_res_header(nmbs, 4);
1094
1095 put_2(nmbs, address);
1096 put_2(nmbs, value);
1097 NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
1098
1099 err = send_msg(nmbs);
1100 if (err != NMBS_ERROR_NONE)
1101 return err;
1102 }
1103 }
1104 else {
1105 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1106 }
1107 }
1108 else {
1109 return recv_write_single_register_res(nmbs, address, value);
1110 }
1111
1112 return NMBS_ERROR_NONE;
1113}
1114#endif
1115
1116
1117#ifndef NMBS_SERVER_WRITE_MULTIPLE_COILS_DISABLED
1118static nmbs_error handle_write_multiple_coils(nmbs_t* nmbs) {
1119 nmbs_error err = recv(nmbs, 5);
1120 if (err != NMBS_ERROR_NONE)
1121 return err;
1122
1123 uint16_t address = get_2(nmbs);
1124 uint16_t quantity = get_2(nmbs);
1125 uint8_t coils_bytes = get_1(nmbs);
1126
1127 NMBS_DEBUG_PRINT("a %d\tq %d\tb %d\tcoils ", address, quantity, coils_bytes);
1128
1129 if (coils_bytes > 246)
1131
1132 err = recv(nmbs, coils_bytes);
1133 if (err != NMBS_ERROR_NONE)
1134 return err;
1135
1136 nmbs_bitfield coils = {0};
1137 for (int i = 0; i < coils_bytes; i++) {
1138 coils[i] = get_1(nmbs);
1139 NMBS_DEBUG_PRINT("%d ", coils[i]);
1140 }
1141
1142 err = recv_msg_footer(nmbs);
1143 if (err != NMBS_ERROR_NONE)
1144 return err;
1145
1146 if (!nmbs->msg.ignored) {
1147 if (quantity < 1 || quantity > 0x07B0)
1148 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1149
1150 if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
1151 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1152
1153 if (coils_bytes == 0)
1154 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1155
1156 if ((quantity + 7) / 8 != coils_bytes)
1157 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1158
1159 if (nmbs->callbacks.write_multiple_coils) {
1160 err = nmbs->callbacks.write_multiple_coils(address, quantity, coils, nmbs->msg.unit_id,
1161 nmbs->callbacks.arg);
1162 if (err != NMBS_ERROR_NONE) {
1163 if (nmbs_error_is_exception(err))
1164 return send_exception_msg(nmbs, err);
1165
1166 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1167 }
1168
1169 if (!nmbs->msg.broadcast) {
1170 put_res_header(nmbs, 4);
1171
1172 put_2(nmbs, address);
1173 put_2(nmbs, quantity);
1174 NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
1175
1176 err = send_msg(nmbs);
1177 if (err != NMBS_ERROR_NONE)
1178 return err;
1179 }
1180 }
1181 else {
1182 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1183 }
1184 }
1185 else {
1186 return recv_write_multiple_coils_res(nmbs, address, quantity);
1187 }
1188
1189 return NMBS_ERROR_NONE;
1190}
1191#endif
1192
1193
1194#ifndef NMBS_SERVER_WRITE_MULTIPLE_REGISTERS_DISABLED
1195static nmbs_error handle_write_multiple_registers(nmbs_t* nmbs) {
1196 nmbs_error err = recv(nmbs, 5);
1197 if (err != NMBS_ERROR_NONE)
1198 return err;
1199
1200 uint16_t address = get_2(nmbs);
1201 uint16_t quantity = get_2(nmbs);
1202 uint8_t registers_bytes = get_1(nmbs);
1203
1204 NMBS_DEBUG_PRINT("a %d\tq %d\tb %d\tregs ", address, quantity, registers_bytes);
1205
1206 err = recv(nmbs, registers_bytes);
1207 if (err != NMBS_ERROR_NONE)
1208 return err;
1209
1210 if (registers_bytes > 246)
1212
1213 uint16_t registers[0x007B];
1214 for (int i = 0; i < registers_bytes / 2; i++) {
1215 registers[i] = get_2(nmbs);
1216 NMBS_DEBUG_PRINT("%d ", registers[i]);
1217 }
1218
1219 err = recv_msg_footer(nmbs);
1220 if (err != NMBS_ERROR_NONE)
1221 return err;
1222
1223 if (!nmbs->msg.ignored) {
1224 if (quantity < 1 || quantity > 0x007B)
1225 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1226
1227 if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
1228 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1229
1230 if (registers_bytes == 0)
1231 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1232
1233 if (registers_bytes != quantity * 2)
1234 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1235
1237 err = nmbs->callbacks.write_multiple_registers(address, quantity, registers, nmbs->msg.unit_id,
1238 nmbs->callbacks.arg);
1239 if (err != NMBS_ERROR_NONE) {
1240 if (nmbs_error_is_exception(err))
1241 return send_exception_msg(nmbs, err);
1242
1243 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1244 }
1245
1246 if (!nmbs->msg.broadcast) {
1247 put_res_header(nmbs, 4);
1248
1249 put_2(nmbs, address);
1250 put_2(nmbs, quantity);
1251 NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
1252
1253 err = send_msg(nmbs);
1254 if (err != NMBS_ERROR_NONE)
1255 return err;
1256 }
1257 }
1258 else {
1259 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1260 }
1261 }
1262 else {
1263 return recv_write_multiple_registers_res(nmbs, address, quantity);
1264 }
1265
1266 return NMBS_ERROR_NONE;
1267}
1268#endif
1269
1270#ifndef NMBS_SERVER_READ_FILE_RECORD_DISABLED
1271static nmbs_error handle_read_file_record(nmbs_t* nmbs) {
1272 nmbs_error err = recv(nmbs, 1);
1273 if (err != NMBS_ERROR_NONE)
1274 return err;
1275
1276 uint8_t request_size = get_1(nmbs);
1277 if (request_size > 245)
1279
1280 err = recv(nmbs, request_size);
1281 if (err != NMBS_ERROR_NONE)
1282 return err;
1283
1284 const uint8_t subreq_header_size = 7;
1285 const uint8_t subreq_count = request_size / subreq_header_size;
1286
1287 struct {
1288 uint8_t reference_type;
1289 uint16_t file_number;
1290 uint16_t record_number;
1291 uint16_t record_length;
1292 }
1293#ifdef __STDC_NO_VLA__
1294 subreq[35]; // 245 / subreq_header_size
1295#else
1296 subreq[subreq_count];
1297#endif
1298
1299 uint8_t response_data_size = 0;
1300
1301 for (uint8_t i = 0; i < subreq_count; i++) {
1302 subreq[i].reference_type = get_1(nmbs);
1303 subreq[i].file_number = get_2(nmbs);
1304 subreq[i].record_number = get_2(nmbs);
1305 subreq[i].record_length = get_2(nmbs);
1306
1307 response_data_size += 2 + subreq[i].record_length * 2;
1308 }
1309
1310 discard_n(nmbs, request_size % subreq_header_size);
1311
1312 err = recv_msg_footer(nmbs);
1313 if (err != NMBS_ERROR_NONE)
1314 return err;
1315
1316 if (!nmbs->msg.ignored) {
1317 if (request_size % subreq_header_size)
1318 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1319
1320 if (request_size < 0x07 || request_size > 0xF5)
1321 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1322
1323 for (uint8_t i = 0; i < subreq_count; i++) {
1324 if (subreq[i].reference_type != 0x06)
1325 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1326
1327 if (subreq[i].file_number == 0x0000)
1328 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1329
1330 if (subreq[i].record_number > 0x270F)
1331 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1332
1333 if (subreq[i].record_length > 124)
1334 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1335
1336 NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fread ", subreq[i].file_number, subreq[i].record_number,
1337 subreq[i].record_length);
1338 }
1339
1340 put_res_header(nmbs, response_data_size);
1341 put_1(nmbs, response_data_size);
1342
1343 if (nmbs->callbacks.read_file_record) {
1344 for (uint8_t i = 0; i < subreq_count; i++) {
1345 uint16_t subreq_data_size = subreq[i].record_length * 2;
1346 put_1(nmbs, subreq_data_size + 1);
1347 put_1(nmbs, 0x06); // add Reference Type const
1348 uint16_t* subreq_data = (uint16_t*) get_n(nmbs, subreq_data_size);
1349
1350 err = nmbs->callbacks.read_file_record(subreq[i].file_number, subreq[i].record_number, subreq_data,
1351 subreq[i].record_length, nmbs->msg.unit_id, nmbs->callbacks.arg);
1352 if (err != NMBS_ERROR_NONE) {
1353 if (nmbs_error_is_exception(err))
1354 return send_exception_msg(nmbs, err);
1355
1356 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1357 }
1358
1359 swap_regs(subreq_data, subreq[i].record_length);
1360 }
1361 }
1362 else {
1363 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1364 }
1365
1366 if (!nmbs->msg.broadcast) {
1367 err = send_msg(nmbs);
1368 if (err != NMBS_ERROR_NONE)
1369 return err;
1370 }
1371 }
1372 else {
1373 return recv_read_file_record_res(nmbs, NULL, 0);
1374 }
1375
1376 return NMBS_ERROR_NONE;
1377}
1378#endif
1379
1380#ifndef NMBS_SERVER_WRITE_FILE_RECORD_DISABLED
1381static nmbs_error handle_write_file_record(nmbs_t* nmbs) {
1382 nmbs_error err = recv(nmbs, 1);
1383 if (err != NMBS_ERROR_NONE)
1384 return err;
1385
1386 uint8_t request_size = get_1(nmbs);
1387 if (request_size > 251) {
1389 }
1390
1391 err = recv(nmbs, request_size);
1392 if (err != NMBS_ERROR_NONE)
1393 return err;
1394
1395 // We can save msg.buf index and use it later for context recovery.
1396 uint16_t msg_buf_idx = nmbs->msg.buf_idx;
1397 discard_n(nmbs, request_size);
1398
1399 err = recv_msg_footer(nmbs);
1400 if (err != NMBS_ERROR_NONE)
1401 return err;
1402
1403 if (!nmbs->msg.ignored) {
1404 const uint8_t subreq_header_size = 7;
1405 uint16_t size = request_size;
1406 nmbs->msg.buf_idx = msg_buf_idx; // restore context
1407
1408 if (request_size < 0x07 || request_size > 0xFB)
1409 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1410
1411 do {
1412 uint8_t subreq_reference_type = get_1(nmbs);
1413 uint16_t subreq_file_number_c = get_2(nmbs);
1414 uint16_t subreq_record_number_c = get_2(nmbs);
1415 uint16_t subreq_record_length_c = get_2(nmbs);
1416 discard_n(nmbs, subreq_record_length_c * 2);
1417
1418 if (subreq_reference_type != 0x06)
1419 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1420
1421 if (subreq_file_number_c == 0x0000)
1422 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1423
1424 if (subreq_record_number_c > 0x270F)
1425 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1426
1427 if (subreq_record_length_c > 122)
1428 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1429
1430 NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fwrite ", subreq_file_number_c, subreq_record_number_c,
1431 subreq_record_length_c);
1432 size -= (subreq_header_size + subreq_record_length_c * 2);
1433 } while (size >= subreq_header_size);
1434
1435 if (size)
1436 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1437
1438 // checks completed
1439
1440 size = request_size;
1441 nmbs->msg.buf_idx = msg_buf_idx; // restore context
1442
1443 do {
1444 discard_1(nmbs);
1445 uint16_t subreq_file_number = get_2(nmbs);
1446 uint16_t subreq_record_number = get_2(nmbs);
1447 uint16_t subreq_record_length = get_2(nmbs);
1448 uint16_t* subreq_data = get_regs(nmbs, subreq_record_length);
1449
1450 if (nmbs->callbacks.write_file_record) {
1451 err = nmbs->callbacks.write_file_record(subreq_file_number, subreq_record_number, subreq_data,
1452 subreq_record_length, nmbs->msg.unit_id, nmbs->callbacks.arg);
1453 if (err != NMBS_ERROR_NONE) {
1454 if (nmbs_error_is_exception(err))
1455 return send_exception_msg(nmbs, err);
1456
1457 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1458 }
1459
1460 swap_regs(subreq_data, subreq_record_length); // restore swapping
1461 }
1462 else {
1463 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1464 }
1465
1466 size -= (subreq_header_size + subreq_record_length * 2);
1467 } while (size >= subreq_header_size);
1468
1469 if (!nmbs->msg.broadcast) {
1470 // The normal response to 'Write File' is an echo of the request.
1471 // We can restore buffer index and response msg.
1472 nmbs->msg.buf_idx = msg_buf_idx;
1473 discard_n(nmbs, request_size);
1474
1475 err = send_msg(nmbs);
1476 if (err != NMBS_ERROR_NONE)
1477 return err;
1478 }
1479 }
1480 else {
1481 return recv_write_file_record_res(nmbs, 0, 0, NULL, 0);
1482 }
1483
1484 return NMBS_ERROR_NONE;
1485}
1486#endif
1487
1488#ifndef NMBS_SERVER_READ_WRITE_REGISTERS_DISABLED
1489static nmbs_error handle_read_write_registers(nmbs_t* nmbs) {
1490 nmbs_error err = recv(nmbs, 9);
1491 if (err != NMBS_ERROR_NONE)
1492 return err;
1493
1494 uint16_t read_address = get_2(nmbs);
1495 uint16_t read_quantity = get_2(nmbs);
1496 uint16_t write_address = get_2(nmbs);
1497 uint16_t write_quantity = get_2(nmbs);
1498
1499 uint8_t byte_count_write = get_1(nmbs);
1500
1501 NMBS_DEBUG_PRINT("ra %d\trq %d\t wa %d\t wq %d\t b %d\tregs ", read_address, read_quantity, write_address,
1502 write_quantity, byte_count_write);
1503
1504 if (byte_count_write > 242)
1506
1507 err = recv(nmbs, byte_count_write);
1508 if (err != NMBS_ERROR_NONE)
1509 return err;
1510
1511#ifdef __STDC_NO_VLA__
1512 uint16_t registers[0x007B];
1513#else
1514 uint16_t registers[byte_count_write / 2];
1515#endif
1516 for (int i = 0; i < byte_count_write / 2; i++) {
1517 registers[i] = get_2(nmbs);
1518 NMBS_DEBUG_PRINT("%d ", registers[i]);
1519 }
1520
1521 err = recv_msg_footer(nmbs);
1522 if (err != NMBS_ERROR_NONE)
1523 return err;
1524
1525 if (!nmbs->msg.ignored) {
1526 if (read_quantity < 1 || read_quantity > 0x007D)
1527 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1528
1529 if (write_quantity < 1 || write_quantity > 0x007B)
1530 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1531
1532 if (byte_count_write != write_quantity * 2)
1533 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1534
1535 if ((uint32_t) read_address + (uint32_t) read_quantity > ((uint32_t) 0xFFFF) + 1)
1536 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1537
1538 if ((uint32_t) write_address + (uint32_t) write_quantity > ((uint32_t) 0xFFFF) + 1)
1539 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1540
1542 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1543
1544 err = nmbs->callbacks.write_multiple_registers(write_address, write_quantity, registers, nmbs->msg.unit_id,
1545 nmbs->callbacks.arg);
1546 if (err != NMBS_ERROR_NONE) {
1547 if (nmbs_error_is_exception(err))
1548 return send_exception_msg(nmbs, err);
1549
1550 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1551 }
1552
1553 if (!nmbs->msg.broadcast) {
1554#ifdef __STDC_NO_VLA__
1555 uint16_t regs[125];
1556#else
1557 uint16_t regs[read_quantity];
1558#endif
1559 err = nmbs->callbacks.read_holding_registers(read_address, read_quantity, regs, nmbs->msg.unit_id,
1560 nmbs->callbacks.arg);
1561 if (err != NMBS_ERROR_NONE) {
1562 if (nmbs_error_is_exception(err))
1563 return send_exception_msg(nmbs, err);
1564
1565 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1566 }
1567
1568 uint8_t regs_bytes = read_quantity * 2;
1569 put_res_header(nmbs, 1 + regs_bytes);
1570
1571 put_1(nmbs, regs_bytes);
1572
1573 NMBS_DEBUG_PRINT("b %d\t", regs_bytes);
1574
1575 NMBS_DEBUG_PRINT("regs ");
1576 for (int i = 0; i < read_quantity; i++) {
1577 put_2(nmbs, regs[i]);
1578 NMBS_DEBUG_PRINT("%d ", regs[i]);
1579 }
1580
1581 err = send_msg(nmbs);
1582 if (err != NMBS_ERROR_NONE)
1583 return err;
1584 }
1585 }
1586 else {
1587 return recv_write_multiple_registers_res(nmbs, write_address, write_quantity);
1588 }
1589
1590 return NMBS_ERROR_NONE;
1591}
1592#endif
1593
1594#ifndef NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED
1595static nmbs_error handle_read_device_identification(nmbs_t* nmbs) {
1596 nmbs_error err = recv(nmbs, 3);
1597 if (err != NMBS_ERROR_NONE)
1598 return err;
1599
1600 uint8_t mei_type = get_1(nmbs);
1601 uint8_t read_device_id_code = get_1(nmbs);
1602 uint8_t object_id = get_1(nmbs);
1603
1604 NMBS_DEBUG_PRINT("c %d\to %d", read_device_id_code, object_id);
1605
1606 err = recv_msg_footer(nmbs);
1607 if (err != NMBS_ERROR_NONE)
1608 return err;
1609
1610 if (!nmbs->msg.ignored) {
1612 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1613
1614 if (mei_type != 0x0E)
1615 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1616
1617 if (read_device_id_code < 1 || read_device_id_code > 4)
1618 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
1619
1620 if (object_id > 6 && object_id < 0x80)
1621 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1622
1623 if (!nmbs->msg.broadcast) {
1625
1628
1630 if (err != NMBS_ERROR_NONE) {
1631 if (nmbs_error_is_exception(err))
1632 return send_exception_msg(nmbs, err);
1633
1634 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1635 }
1636
1637 put_res_header(nmbs, 0); // Length will be set later
1638 put_1(nmbs, 0x0E);
1639 put_1(nmbs, read_device_id_code);
1640 put_1(nmbs, 0x83);
1641
1642 if (read_device_id_code == 4) {
1643 if (!nmbs_bitfield_read(map, object_id))
1644 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1645
1646 put_1(nmbs, 0); // More follows
1647 put_1(nmbs, 0); // Next Object Id
1648 put_1(nmbs, 1); // Number of objects
1649
1650 str[0] = 0;
1651 err = nmbs->callbacks.read_device_identification(object_id, str);
1652 if (err != NMBS_ERROR_NONE) {
1653 if (nmbs_error_is_exception(err))
1654 return send_exception_msg(nmbs, err);
1655
1656 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1657 }
1658
1659 size_t str_len = strlen(str);
1660
1661 put_1(nmbs, object_id); // Object id
1662 put_1(nmbs, str_len); // Object length
1663 put_n(nmbs, (uint8_t*) str, str_len);
1664
1665 set_msg_header_size(nmbs, 6 + 2 + str_len);
1666
1667 return send_msg(nmbs);
1668 }
1669
1670 uint8_t more_follows_idx = nmbs->msg.buf_idx;
1671 put_1(nmbs, 0);
1672 uint8_t next_object_id_idx = nmbs->msg.buf_idx;
1673 put_1(nmbs, 0);
1674 uint8_t number_of_objects_idx = nmbs->msg.buf_idx;
1675 put_1(nmbs, 0);
1676
1677 int16_t res_size_left = 253 - 7;
1678
1679 uint8_t last_id = 0;
1680 uint8_t msg_size = 6;
1681 uint8_t res_more_follows = 0;
1682 uint8_t res_next_object_id = 0;
1683 uint8_t res_number_of_objects = 0;
1684
1685 switch (read_device_id_code) {
1686 case 1:
1687 if (object_id > 0x02)
1688 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1689 last_id = 0x02;
1690 break;
1691 case 2:
1692 if (object_id < 0x03 || object_id > 0x07)
1693 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1694 last_id = 0x07;
1695 break;
1696 case 3:
1697 if (object_id < 0x80)
1698 return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
1699 last_id = 0xFF;
1700 break;
1701 default:
1702 // Unreachable
1703 break;
1704 }
1705
1706 for (uint16_t id = object_id; id <= last_id; id++) {
1707 if (!nmbs_bitfield_read(map, id)) {
1708 if (id < 0x03)
1709 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1710 continue;
1711 }
1712
1713 str[0] = 0;
1714 err = nmbs->callbacks.read_device_identification((uint8_t) id, str);
1715 if (err != NMBS_ERROR_NONE) {
1716 if (nmbs_error_is_exception(err))
1717 return send_exception_msg(nmbs, err);
1718
1719 return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
1720 }
1721
1722 int16_t str_len = (int16_t) strlen(str);
1723
1724 res_size_left = (int16_t) (res_size_left - 2 - str_len);
1725 if (res_size_left < 0) {
1726 res_more_follows = 0xFF;
1727 res_next_object_id = id;
1728 break;
1729 }
1730
1731 put_1(nmbs, (uint8_t) id); // Object id
1732 put_1(nmbs, str_len); // Object length
1733 put_n(nmbs, (uint8_t*) str, str_len);
1734
1735 msg_size += (2 + str_len);
1736
1737 res_number_of_objects++;
1738 }
1739
1740 set_1(nmbs, res_more_follows, more_follows_idx);
1741 set_1(nmbs, res_next_object_id, next_object_id_idx);
1742 set_1(nmbs, res_number_of_objects, number_of_objects_idx);
1743
1744 set_msg_header_size(nmbs, msg_size);
1745
1746 return send_msg(nmbs);
1747 }
1748 }
1749 else {
1750 return recv_read_device_identification_res(nmbs, 0, NULL, 0, NULL, NULL, NULL, NULL);
1751 }
1752
1753 return NMBS_ERROR_NONE;
1754}
1755#endif
1756
1757
1758static nmbs_error handle_req_fc(nmbs_t* nmbs) {
1759 NMBS_DEBUG_PRINT("fc %d\t", nmbs->msg.fc);
1760
1762 switch (nmbs->msg.fc) {
1763#ifndef NMBS_SERVER_READ_COILS_DISABLED
1764 case 1:
1765 err = handle_read_coils(nmbs);
1766 break;
1767#endif
1768
1769#ifndef NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED
1770 case 2:
1771 err = handle_read_discrete_inputs(nmbs);
1772 break;
1773#endif
1774
1775#ifndef NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED
1776 case 3:
1777 err = handle_read_holding_registers(nmbs);
1778 break;
1779#endif
1780
1781#ifndef NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED
1782 case 4:
1783 err = handle_read_input_registers(nmbs);
1784 break;
1785#endif
1786
1787#ifndef NMBS_SERVER_WRITE_SINGLE_COIL_DISABLED
1788 case 5:
1789 err = handle_write_single_coil(nmbs);
1790 break;
1791#endif
1792
1793#ifndef NMBS_SERVER_WRITE_SINGLE_REGISTER_DISABLED
1794 case 6:
1795 err = handle_write_single_register(nmbs);
1796 break;
1797#endif
1798
1799#ifndef NMBS_SERVER_WRITE_MULTIPLE_COILS_DISABLED
1800 case 15:
1801 err = handle_write_multiple_coils(nmbs);
1802 break;
1803#endif
1804
1805#ifndef NMBS_SERVER_WRITE_MULTIPLE_REGISTERS_DISABLED
1806 case 16:
1807 err = handle_write_multiple_registers(nmbs);
1808 break;
1809#endif
1810
1811#ifndef NMBS_SERVER_READ_FILE_RECORD_DISABLED
1812 case 20:
1813 err = handle_read_file_record(nmbs);
1814 break;
1815#endif
1816
1817#ifndef NMBS_SERVER_WRITE_FILE_RECORD_DISABLED
1818 case 21:
1819 err = handle_write_file_record(nmbs);
1820 break;
1821#endif
1822
1823#ifndef NMBS_SERVER_READ_WRITE_REGISTERS_DISABLED
1824 case 23:
1825 err = handle_read_write_registers(nmbs);
1826 break;
1827#endif
1828
1829#ifndef NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED
1830 case 43:
1831 err = handle_read_device_identification(nmbs);
1832 break;
1833#endif
1834 default:
1835 err = send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
1836 }
1837
1838 return err;
1839}
1840
1841
1842nmbs_error nmbs_server_create(nmbs_t* nmbs, uint8_t address_rtu, const nmbs_platform_conf* platform_conf,
1843 const nmbs_callbacks* callbacks) {
1844 if (platform_conf->transport == NMBS_TRANSPORT_RTU && address_rtu == 0)
1846
1847 nmbs_error ret = nmbs_create(nmbs, platform_conf);
1848 if (ret != NMBS_ERROR_NONE)
1849 return ret;
1850
1851 nmbs->address_rtu = address_rtu;
1852 nmbs->callbacks = *callbacks;
1853
1854 return NMBS_ERROR_NONE;
1855}
1856
1857
1859 msg_state_reset(nmbs);
1860
1861 bool first_byte_received = false;
1862 nmbs_error err = recv_req_header(nmbs, &first_byte_received);
1863 if (err != NMBS_ERROR_NONE) {
1864 if (!first_byte_received && err == NMBS_ERROR_TIMEOUT)
1865 return NMBS_ERROR_NONE;
1866
1867 return err;
1868 }
1869
1870#ifdef NMBS_DEBUG
1871 printf("%d ", nmbs->address_rtu);
1872 printf("NMBS req <- ");
1873 if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
1874 if (nmbs->msg.broadcast)
1875 printf("broadcast\t");
1876 else
1877 printf("address_rtu %d\t", nmbs->msg.unit_id);
1878 }
1879#endif
1880
1881 err = handle_req_fc(nmbs);
1882 if (err != NMBS_ERROR_NONE && !nmbs_error_is_exception(err)) {
1883 if (nmbs->platform.transport == NMBS_TRANSPORT_RTU && err != NMBS_ERROR_TIMEOUT && nmbs->msg.ignored) {
1884 // Flush the remaining data on the line
1885 nmbs->platform.read(nmbs->msg.buf, sizeof(nmbs->msg.buf), 0, nmbs->platform.arg);
1886 }
1887
1888 return err;
1889 }
1890
1891 return NMBS_ERROR_NONE;
1892}
1893
1894void nmbs_set_callbacks_arg(nmbs_t* nmbs, void* arg) {
1895 nmbs->callbacks.arg = arg;
1896}
1897#endif
1898
1899
1900#ifndef NMBS_CLIENT_DISABLED
1902 return nmbs_create(nmbs, platform_conf);
1903}
1904
1905
1906static nmbs_error read_discrete(nmbs_t* nmbs, uint8_t fc, uint16_t address, uint16_t quantity, nmbs_bitfield values) {
1907 if (quantity < 1 || quantity > 2000)
1909
1910 if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
1912
1913 msg_state_req(nmbs, fc);
1914 put_req_header(nmbs, 4);
1915
1916 put_2(nmbs, address);
1917 put_2(nmbs, quantity);
1918
1919 NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
1920
1921 nmbs_error err = send_msg(nmbs);
1922 if (err != NMBS_ERROR_NONE)
1923 return err;
1924
1925 return recv_read_discrete_res(nmbs, values);
1926}
1927
1928
1929nmbs_error nmbs_read_coils(nmbs_t* nmbs, uint16_t address, uint16_t quantity, nmbs_bitfield coils_out) {
1930 return read_discrete(nmbs, 1, address, quantity, coils_out);
1931}
1932
1933
1934nmbs_error nmbs_read_discrete_inputs(nmbs_t* nmbs, uint16_t address, uint16_t quantity, nmbs_bitfield inputs_out) {
1935 return read_discrete(nmbs, 2, address, quantity, inputs_out);
1936}
1937
1938static nmbs_error read_registers(nmbs_t* nmbs, uint8_t fc, uint16_t address, uint16_t quantity, uint16_t* registers) {
1939 if (quantity < 1 || quantity > 125)
1941
1942 if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
1944
1945 msg_state_req(nmbs, fc);
1946 put_req_header(nmbs, 4);
1947
1948 put_2(nmbs, address);
1949 put_2(nmbs, quantity);
1950
1951 NMBS_DEBUG_PRINT("a %d\tq %d ", address, quantity);
1952
1953 nmbs_error err = send_msg(nmbs);
1954 if (err != NMBS_ERROR_NONE)
1955 return err;
1956
1957 return recv_read_registers_res(nmbs, quantity, registers);
1958}
1959
1960
1961nmbs_error nmbs_read_holding_registers(nmbs_t* nmbs, uint16_t address, uint16_t quantity, uint16_t* registers_out) {
1962 return read_registers(nmbs, 3, address, quantity, registers_out);
1963}
1964
1965
1966nmbs_error nmbs_read_input_registers(nmbs_t* nmbs, uint16_t address, uint16_t quantity, uint16_t* registers_out) {
1967 return read_registers(nmbs, 4, address, quantity, registers_out);
1968}
1969
1970nmbs_error nmbs_write_single_coil(nmbs_t *nmbs, uint16_t address, uint16_t value)
1971{
1972 msg_state_req(nmbs, 5);
1973 put_req_header(nmbs, 4);
1974
1975 put_2(nmbs, address);
1976 put_2(nmbs, value);
1977
1978 NMBS_DEBUG_PRINT("a %d\tvalue %d ", address, value);
1979
1980 nmbs_error err = send_msg(nmbs);
1981 if (err != NMBS_ERROR_NONE)
1982 return err;
1983
1984 if (!nmbs->msg.broadcast)
1985 return recv_write_single_coil_res(nmbs, address, value);
1986
1987 return NMBS_ERROR_NONE;
1988}
1989
1990nmbs_error nmbs_write_single_register(nmbs_t* nmbs, uint16_t address, uint16_t value) {
1991 msg_state_req(nmbs, 6);
1992 put_req_header(nmbs, 4);
1993
1994 put_2(nmbs, address);
1995 put_2(nmbs, value);
1996
1997 NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
1998
1999 nmbs_error err = send_msg(nmbs);
2000 if (err != NMBS_ERROR_NONE)
2001 return err;
2002
2003 if (!nmbs->msg.broadcast)
2004 return recv_write_single_register_res(nmbs, address, value);
2005
2006 return NMBS_ERROR_NONE;
2007}
2008
2009
2010nmbs_error nmbs_write_multiple_coils(nmbs_t* nmbs, uint16_t address, uint16_t quantity, const nmbs_bitfield coils) {
2011 if (quantity < 1 || quantity > 0x07B0)
2013
2014 if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
2016
2017 uint8_t coils_bytes = (quantity + 7) / 8;
2018
2019 msg_state_req(nmbs, 15);
2020 put_req_header(nmbs, 5 + coils_bytes);
2021
2022 put_2(nmbs, address);
2023 put_2(nmbs, quantity);
2024 put_1(nmbs, coils_bytes);
2025 NMBS_DEBUG_PRINT("a %d\tq %d\tb %d\t", address, quantity, coils_bytes);
2026
2027 NMBS_DEBUG_PRINT("coils ");
2028 for (int i = 0; i < coils_bytes; i++) {
2029 put_1(nmbs, coils[i]);
2030 NMBS_DEBUG_PRINT("%d ", coils[i]);
2031 }
2032
2033 nmbs_error err = send_msg(nmbs);
2034 if (err != NMBS_ERROR_NONE)
2035 return err;
2036
2037 if (!nmbs->msg.broadcast)
2038 return recv_write_multiple_coils_res(nmbs, address, quantity);
2039
2040 return NMBS_ERROR_NONE;
2041}
2042
2043
2044nmbs_error nmbs_write_multiple_registers(nmbs_t* nmbs, uint16_t address, uint16_t quantity, const uint16_t* registers) {
2045 if (quantity < 1 || quantity > 0x007B)
2047
2048 if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
2050
2051 uint8_t registers_bytes = quantity * 2;
2052
2053 msg_state_req(nmbs, 16);
2054 put_req_header(nmbs, 5 + registers_bytes);
2055
2056 put_2(nmbs, address);
2057 put_2(nmbs, quantity);
2058 put_1(nmbs, registers_bytes);
2059 NMBS_DEBUG_PRINT("a %d\tq %d\tb %d\t", address, quantity, registers_bytes);
2060
2061 NMBS_DEBUG_PRINT("regs ");
2062 for (int i = 0; i < quantity; i++) {
2063 put_2(nmbs, registers[i]);
2064 NMBS_DEBUG_PRINT("%d ", registers[i]);
2065 }
2066
2067 nmbs_error err = send_msg(nmbs);
2068 if (err != NMBS_ERROR_NONE)
2069 return err;
2070
2071 if (!nmbs->msg.broadcast)
2072 return recv_write_single_register_res(nmbs, address, quantity);
2073
2074 return NMBS_ERROR_NONE;
2075}
2076
2077
2078nmbs_error nmbs_read_file_record(nmbs_t* nmbs, uint16_t file_number, uint16_t record_number, uint16_t* registers,
2079 uint16_t count) {
2080 if (file_number == 0x0000)
2082
2083 if (record_number > 0x270F)
2085
2086 // In expected response: max PDU length = 253, assuming a single file request, (253 - 1 - 1 - 1 - 1) / 2 = 124
2087 if (count > 124)
2089
2090 msg_state_req(nmbs, 20);
2091 put_req_header(nmbs, 8);
2092
2093 put_1(nmbs, 7); // add Byte Count
2094 put_1(nmbs, 6); // add Reference Type const
2095 put_2(nmbs, file_number);
2096 put_2(nmbs, record_number);
2097 put_2(nmbs, count);
2098 NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fread ", file_number, record_number, count);
2099
2100 nmbs_error err = send_msg(nmbs);
2101 if (err != NMBS_ERROR_NONE)
2102 return err;
2103
2104 return recv_read_file_record_res(nmbs, registers, count);
2105}
2106
2107
2108nmbs_error nmbs_write_file_record(nmbs_t* nmbs, uint16_t file_number, uint16_t record_number, const uint16_t* registers,
2109 uint16_t count) {
2110 if (file_number == 0x0000)
2112
2113 if (record_number > 0x270F)
2115
2116 if (count > 122)
2118
2119 uint16_t data_size = count * 2;
2120
2121 msg_state_req(nmbs, 21);
2122 put_req_header(nmbs, 8 + data_size);
2123
2124 put_1(nmbs, 7 + data_size); // add Byte Count
2125 put_1(nmbs, 6); // add Reference Type const
2126 put_2(nmbs, file_number);
2127 put_2(nmbs, record_number);
2128 put_2(nmbs, count);
2129 put_regs(nmbs, registers, count);
2130 NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fwrite ", file_number, record_number, count);
2131
2132 nmbs_error err = send_msg(nmbs);
2133 if (err != NMBS_ERROR_NONE)
2134 return err;
2135
2136 if (!nmbs->msg.broadcast)
2137 return recv_write_file_record_res(nmbs, file_number, record_number, registers, count);
2138
2139 return NMBS_ERROR_NONE;
2140}
2141
2142
2143nmbs_error nmbs_read_write_registers(nmbs_t* nmbs, uint16_t read_address, uint16_t read_quantity,
2144 uint16_t* registers_out, uint16_t write_address, uint16_t write_quantity,
2145 const uint16_t* registers) {
2146 if (read_quantity < 1 || read_quantity > 0x007D)
2148
2149 if ((uint32_t) read_address + (uint32_t) read_quantity > ((uint32_t) 0xFFFF) + 1)
2151
2152 if (write_quantity < 1 || write_quantity > 0x0079)
2154
2155 if ((uint32_t) write_address + (uint32_t) write_quantity > ((uint32_t) 0xFFFF) + 1)
2157
2158 uint8_t registers_bytes = write_quantity * 2;
2159
2160 msg_state_req(nmbs, 23);
2161 put_req_header(nmbs, 9 + registers_bytes);
2162
2163 put_2(nmbs, read_address);
2164 put_2(nmbs, read_quantity);
2165 put_2(nmbs, write_address);
2166 put_2(nmbs, write_quantity);
2167 put_1(nmbs, registers_bytes);
2168
2169 NMBS_DEBUG_PRINT("read a %d\tq %d ", read_address, read_quantity);
2170 NMBS_DEBUG_PRINT("write a %d\tq %d\tb %d\t", write_address, write_quantity, registers_bytes);
2171
2172 NMBS_DEBUG_PRINT("regs ");
2173 for (int i = 0; i < write_quantity; i++) {
2174 put_2(nmbs, registers[i]);
2175 NMBS_DEBUG_PRINT("%d ", registers[i]);
2176 }
2177
2178 nmbs_error err = send_msg(nmbs);
2179 if (err != NMBS_ERROR_NONE)
2180 return err;
2181
2182 if (!nmbs->msg.broadcast) {
2183 return recv_read_registers_res(nmbs, read_quantity, registers_out);
2184 }
2185
2186 return NMBS_ERROR_NONE;
2187}
2188
2189
2190nmbs_error nmbs_read_device_identification_basic(nmbs_t* nmbs, char* vendor_name, char* product_code,
2191 char* major_minor_revision, uint8_t buffers_length) {
2192 const uint8_t order[3] = {0, 1, 2};
2193 char* buffers[3] = {vendor_name, product_code, major_minor_revision};
2194 uint8_t total_received = 0;
2195 uint8_t next_object_id = 0x00;
2196
2197 while (next_object_id != 0x7F) {
2198 msg_state_req(nmbs, 43);
2199 put_msg_header(nmbs, 3);
2200 put_1(nmbs, 0x0E);
2201 put_1(nmbs, 1);
2202 put_1(nmbs, next_object_id);
2203
2204 nmbs_error err = send_msg(nmbs);
2205 if (err != NMBS_ERROR_NONE)
2206 return err;
2207
2208 uint8_t objects_received = 0;
2209 err = recv_read_device_identification_res(nmbs, 3, buffers, buffers_length, order, NULL, &next_object_id,
2210 &objects_received);
2211 if (err != NMBS_ERROR_NONE)
2212 return err;
2213
2214 total_received += objects_received;
2215 if (total_received > 3)
2217
2218 if (objects_received == 0)
2220 }
2221
2222 return NMBS_ERROR_NONE;
2223}
2224
2225
2226nmbs_error nmbs_read_device_identification_regular(nmbs_t* nmbs, char* vendor_url, char* product_name, char* model_name,
2227 char* user_application_name, uint8_t buffers_length) {
2228 const uint8_t order[7] = {0, 0, 0, 0, 1, 2, 3};
2229 char* buffers[4] = {vendor_url, product_name, model_name, user_application_name};
2230 uint8_t total_received = 0;
2231 uint8_t next_object_id = 0x03;
2232
2233 while (next_object_id != 0x7F) {
2234 msg_state_req(nmbs, 43);
2235 put_req_header(nmbs, 3);
2236 put_1(nmbs, 0x0E);
2237 put_1(nmbs, 2);
2238 put_1(nmbs, next_object_id);
2239
2240 nmbs_error err = send_msg(nmbs);
2241 if (err != NMBS_ERROR_NONE)
2242 return err;
2243
2244 uint8_t objects_received = 0;
2245 err = recv_read_device_identification_res(nmbs, 4, buffers, buffers_length, order, NULL, &next_object_id,
2246 &objects_received);
2247 if (err != NMBS_ERROR_NONE)
2248 return err;
2249
2250 total_received += objects_received;
2251 if (total_received > 4)
2253
2254 if (objects_received == 0)
2256 }
2257
2258 return NMBS_ERROR_NONE;
2259}
2260
2261
2262nmbs_error nmbs_read_device_identification_extended(nmbs_t* nmbs, uint8_t object_id_start, uint8_t* ids, char** buffers,
2263 uint8_t ids_length, uint8_t buffer_length,
2264 uint8_t* objects_count_out) {
2265 if (object_id_start < 0x80)
2267
2268 uint8_t total_received = 0;
2269 uint8_t next_object_id = object_id_start;
2270
2271 while (next_object_id != 0x7F) {
2272 msg_state_req(nmbs, 43);
2273 put_req_header(nmbs, 3);
2274 put_1(nmbs, 0x0E);
2275 put_1(nmbs, 3);
2276 put_1(nmbs, next_object_id);
2277
2278 nmbs_error err = send_msg(nmbs);
2279 if (err != NMBS_ERROR_NONE)
2280 return err;
2281
2282 uint8_t objects_received = 0;
2283 err = recv_read_device_identification_res(nmbs, ids_length - total_received, &buffers[total_received],
2284 buffer_length, NULL, &ids[total_received], &next_object_id,
2285 &objects_received);
2286 if (err != NMBS_ERROR_NONE)
2287 return err;
2288
2289 total_received += objects_received;
2290 }
2291
2292 *objects_count_out = total_received;
2293
2294 return NMBS_ERROR_NONE;
2295}
2296
2297
2298nmbs_error nmbs_read_device_identification(nmbs_t* nmbs, uint8_t object_id, char* buffer, uint8_t buffer_length) {
2299 if (object_id > 0x06 && object_id < 0x80)
2301
2302 msg_state_req(nmbs, 43);
2303 put_req_header(nmbs, 3);
2304 put_1(nmbs, 0x0E);
2305 put_1(nmbs, 4);
2306 put_1(nmbs, object_id);
2307
2308 nmbs_error err = send_msg(nmbs);
2309 if (err != NMBS_ERROR_NONE)
2310 return err;
2311
2312 char* buf[1] = {buffer};
2313 return recv_read_device_identification_res(nmbs, 1, buf, buffer_length, NULL, NULL, NULL, NULL);
2314}
2315
2316
2317nmbs_error nmbs_send_raw_pdu(nmbs_t* nmbs, uint8_t fc, const uint8_t* data, uint16_t data_len) {
2318 msg_state_req(nmbs, fc);
2319 put_msg_header(nmbs, data_len);
2320
2321 NMBS_DEBUG_PRINT("raw ");
2322 for (uint16_t i = 0; i < data_len; i++) {
2323 put_1(nmbs, data[i]);
2324 NMBS_DEBUG_PRINT("%d ", data[i]);
2325 }
2326
2327 return send_msg(nmbs);
2328}
2329
2330
2331nmbs_error nmbs_receive_raw_pdu_response(nmbs_t* nmbs, uint8_t* data_out, uint8_t data_out_len) {
2332 nmbs_error err = recv_res_header(nmbs);
2333 if (err != NMBS_ERROR_NONE)
2334 return err;
2335
2336 err = recv(nmbs, data_out_len);
2337 if (err != NMBS_ERROR_NONE)
2338 return err;
2339
2340 if (data_out) {
2341 for (uint16_t i = 0; i < data_out_len; i++)
2342 data_out[i] = get_1(nmbs);
2343 }
2344 else {
2345 for (uint16_t i = 0; i < data_out_len; i++)
2346 get_1(nmbs);
2347 }
2348
2349 err = recv_msg_footer(nmbs);
2350 if (err != NMBS_ERROR_NONE)
2351 return err;
2352
2353 return NMBS_ERROR_NONE;
2354}
2355#endif
2356
2357
2358#ifndef NMBS_STRERROR_DISABLED
2359const char* nmbs_strerror(nmbs_error error) {
2360 switch (error) {
2362 return "invalid request received";
2363
2365 return "invalid unit ID received";
2366
2368 return "invalid TCP MBAP received";
2369
2370 case NMBS_ERROR_CRC:
2371 return "invalid CRC received";
2372
2374 return "transport error";
2375
2376 case NMBS_ERROR_TIMEOUT:
2377 return "timeout";
2378
2380 return "invalid response received";
2381
2383 return "invalid argument provided";
2384
2385 case NMBS_ERROR_NONE:
2386 return "no error";
2387
2389 return "modbus exception 1: illegal function";
2390
2392 return "modbus exception 2: illegal data address";
2393
2395 return "modbus exception 3: data value";
2396
2398 return "modbus exception 4: server device failure";
2399
2400 default:
2401 return "unknown error";
2402 }
2403}
2404#endif
nmbs_error nmbs_read_input_registers(nmbs_t *nmbs, uint16_t address, uint16_t quantity, uint16_t *registers_out)
nmbs_error nmbs_write_multiple_registers(nmbs_t *nmbs, uint16_t address, uint16_t quantity, const uint16_t *registers)
nmbs_error nmbs_write_single_register(nmbs_t *nmbs, uint16_t address, uint16_t value)
nmbs_error nmbs_send_raw_pdu(nmbs_t *nmbs, uint8_t fc, const uint8_t *data, uint16_t data_len)
nmbs_error recv_read_device_identification_res(nmbs_t *nmbs, uint8_t buffers_count, char **buffers_out, uint8_t buffers_length, const uint8_t *order, uint8_t *ids_out, uint8_t *next_object_id_out, uint8_t *objects_count_out)
Definition nanomodbus.c:773
nmbs_error nmbs_client_create(nmbs_t *nmbs, const nmbs_platform_conf *platform_conf)
nmbs_error recv_write_file_record_res(nmbs_t *nmbs, uint16_t file_number, uint16_t record_number, const uint16_t *registers, uint16_t count)
Definition nanomodbus.c:721
nmbs_error nmbs_read_device_identification_regular(nmbs_t *nmbs, char *vendor_url, char *product_name, char *model_name, char *user_application_name, uint8_t buffers_length)
nmbs_error recv_read_file_record_res(nmbs_t *nmbs, uint16_t *registers, uint16_t count)
Definition nanomodbus.c:680
void nmbs_set_byte_timeout(nmbs_t *nmbs, int32_t timeout_ms)
Definition nanomodbus.c:201
nmbs_error recv_write_multiple_registers_res(nmbs_t *nmbs, uint16_t address, uint16_t quantity)
Definition nanomodbus.c:653
nmbs_error nmbs_read_holding_registers(nmbs_t *nmbs, uint16_t address, uint16_t quantity, uint16_t *registers_out)
void nmbs_set_destination_rtu_address(nmbs_t *nmbs, uint8_t address)
Definition nanomodbus.c:206
nmbs_error nmbs_receive_raw_pdu_response(nmbs_t *nmbs, uint8_t *data_out, uint8_t data_out_len)
nmbs_error nmbs_server_poll(nmbs_t *nmbs)
const char * nmbs_strerror(nmbs_error error)
nmbs_error nmbs_read_device_identification_basic(nmbs_t *nmbs, char *vendor_name, char *product_code, char *major_minor_revision, uint8_t buffers_length)
nmbs_error recv_write_multiple_coils_res(nmbs_t *nmbs, uint16_t address, uint16_t quantity)
Definition nanomodbus.c:626
nmbs_error nmbs_write_file_record(nmbs_t *nmbs, uint16_t file_number, uint16_t record_number, const uint16_t *registers, uint16_t count)
nmbs_error nmbs_write_single_coil(nmbs_t *nmbs, uint16_t address, uint16_t value)
nmbs_error nmbs_write_multiple_coils(nmbs_t *nmbs, uint16_t address, uint16_t quantity, const nmbs_bitfield coils)
void nmbs_set_callbacks_arg(nmbs_t *nmbs, void *arg)
nmbs_error nmbs_read_device_identification_extended(nmbs_t *nmbs, uint8_t object_id_start, uint8_t *ids, char **buffers, uint8_t ids_length, uint8_t buffer_length, uint8_t *objects_count_out)
nmbs_error nmbs_server_create(nmbs_t *nmbs, uint8_t address_rtu, const nmbs_platform_conf *platform_conf, const nmbs_callbacks *callbacks)
nmbs_error nmbs_create(nmbs_t *nmbs, const nmbs_platform_conf *platform_conf)
Definition nanomodbus.c:172
nmbs_error recv_write_single_register_res(nmbs_t *nmbs, uint16_t address, uint16_t value_req)
Definition nanomodbus.c:599
nmbs_error nmbs_read_coils(nmbs_t *nmbs, uint16_t address, uint16_t quantity, nmbs_bitfield coils_out)
nmbs_error nmbs_read_file_record(nmbs_t *nmbs, uint16_t file_number, uint16_t record_number, uint16_t *registers, uint16_t count)
void nmbs_set_read_timeout(nmbs_t *nmbs, int32_t timeout_ms)
Definition nanomodbus.c:196
void nmbs_set_platform_arg(nmbs_t *nmbs, void *arg)
Definition nanomodbus.c:211
nmbs_error nmbs_read_write_registers(nmbs_t *nmbs, uint16_t read_address, uint16_t read_quantity, uint16_t *registers_out, uint16_t write_address, uint16_t write_quantity, const uint16_t *registers)
nmbs_error recv_write_single_coil_res(nmbs_t *nmbs, uint16_t address, uint16_t value_req)
Definition nanomodbus.c:571
uint16_t nmbs_crc_calc(const uint8_t *data, uint32_t length)
Definition nanomodbus.c:216
#define NMBS_DEBUG_PRINT(...)
Definition nanomodbus.c:37
nmbs_error nmbs_read_discrete_inputs(nmbs_t *nmbs, uint16_t address, uint16_t quantity, nmbs_bitfield inputs_out)
nmbs_error nmbs_read_device_identification(nmbs_t *nmbs, uint8_t object_id, char *buffer, uint8_t buffer_length)
nmbs_error
Definition nanomodbus.h:54
@ NMBS_ERROR_INVALID_UNIT_ID
Definition nanomodbus.h:57
@ NMBS_ERROR_NONE
Definition nanomodbus.h:64
@ NMBS_ERROR_INVALID_ARGUMENT
Definition nanomodbus.h:63
@ NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS
Definition nanomodbus.h:68
@ NMBS_ERROR_TRANSPORT
Definition nanomodbus.h:60
@ NMBS_ERROR_INVALID_TCP_MBAP
Definition nanomodbus.h:58
@ NMBS_ERROR_INVALID_REQUEST
Definition nanomodbus.h:56
@ NMBS_ERROR_INVALID_RESPONSE
Definition nanomodbus.h:62
@ NMBS_EXCEPTION_ILLEGAL_DATA_VALUE
Definition nanomodbus.h:69
@ NMBS_ERROR_TIMEOUT
Definition nanomodbus.h:61
@ NMBS_EXCEPTION_ILLEGAL_FUNCTION
Definition nanomodbus.h:67
@ NMBS_EXCEPTION_SERVER_DEVICE_FAILURE
Definition nanomodbus.h:70
@ NMBS_ERROR_CRC
Definition nanomodbus.h:59
uint8_t nmbs_bitfield[250]
Definition nanomodbus.h:84
#define nmbs_bitfield_reset(bf)
Definition nanomodbus.h:115
#define nmbs_bitfield_read(bf, b)
Definition nanomodbus.h:94
#define NMBS_DEVICE_IDENTIFICATION_STRING_LENGTH
Definition nanomodbus.h:213
@ NMBS_TRANSPORT_TCP
Definition nanomodbus.h:122
@ NMBS_TRANSPORT_RTU
Definition nanomodbus.h:121
uint8_t nmbs_bitfield_256[32]
Definition nanomodbus.h:89
#define nmbs_error_is_exception(e)
Definition nanomodbus.h:78
nmbs_error(* write_single_coil)(uint16_t address, bool value, uint8_t unit_id, void *arg)
Definition nanomodbus.h:185
nmbs_error(* write_multiple_registers)(uint16_t address, uint16_t quantity, const uint16_t *registers, uint8_t unit_id, void *arg)
Definition nanomodbus.h:198
nmbs_error(* write_file_record)(uint16_t file_number, uint16_t record_number, const uint16_t *registers, uint16_t count, uint8_t unit_id, void *arg)
Definition nanomodbus.h:208
nmbs_error(* read_input_registers)(uint16_t address, uint16_t quantity, uint16_t *registers_out, uint8_t unit_id, void *arg)
Definition nanomodbus.h:180
nmbs_error(* read_file_record)(uint16_t file_number, uint16_t record_number, uint16_t *registers, uint16_t count, uint8_t unit_id, void *arg)
Definition nanomodbus.h:203
nmbs_error(* read_coils)(uint16_t address, uint16_t quantity, nmbs_bitfield coils_out, uint8_t unit_id, void *arg)
Definition nanomodbus.h:166
nmbs_error(* read_discrete_inputs)(uint16_t address, uint16_t quantity, nmbs_bitfield inputs_out, uint8_t unit_id, void *arg)
Definition nanomodbus.h:170
nmbs_error(* write_single_register)(uint16_t address, uint16_t value, uint8_t unit_id, void *arg)
Definition nanomodbus.h:189
nmbs_error(* read_holding_registers)(uint16_t address, uint16_t quantity, uint16_t *registers_out, uint8_t unit_id, void *arg)
Definition nanomodbus.h:175
nmbs_error(* read_device_identification_map)(nmbs_bitfield_256 map)
Definition nanomodbus.h:215
nmbs_error(* read_device_identification)(uint8_t object_id, char buffer[NMBS_DEVICE_IDENTIFICATION_STRING_LENGTH])
Definition nanomodbus.h:214
nmbs_error(* write_multiple_coils)(uint16_t address, uint16_t quantity, const nmbs_bitfield coils, uint8_t unit_id, void *arg)
Definition nanomodbus.h:193
int32_t(* read)(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
Definition nanomodbus.h:147
nmbs_transport transport
Definition nanomodbus.h:146
int32_t(* write)(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
Definition nanomodbus.h:149
bool broadcast
Definition nanomodbus.h:235
int32_t read_timeout_ms
Definition nanomodbus.h:242
uint8_t dest_address_rtu
Definition nanomodbus.h:247
bool ignored
Definition nanomodbus.h:236
uint8_t address_rtu
Definition nanomodbus.h:246
uint8_t unit_id
Definition nanomodbus.h:232
uint16_t transaction_id
Definition nanomodbus.h:234
struct nmbs_t::@501 msg
nmbs_callbacks callbacks
Definition nanomodbus.h:239
nmbs_platform_conf platform
Definition nanomodbus.h:244
int32_t byte_timeout_ms
Definition nanomodbus.h:241
uint8_t buf[260]
Definition nanomodbus.h:229
uint8_t fc
Definition nanomodbus.h:233
uint16_t buf_idx
Definition nanomodbus.h:230
uint16_t current_tid
Definition nanomodbus.h:248