2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2015 Bart de Waal <bart@waalamo.com>
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 3 of the License, or
9 ## (at your option) any later version.
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ## GNU General Public License for more details.
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
20 import sigrokdecode as srd
25 rxtx_channels = ('RX', 'TX')
27 class No_more_data(Exception):
28 '''This exception is a signal that we should stop parsing an ADU as there
29 is no more data to parse.'''
33 '''The Data class is used to hold the bytes from the serial decode.'''
34 def __init__(self, start, end, data):
40 '''An Application Data Unit is what Modbus calls one message.
41 Protocol decoders are supposed to keep track of state and then provide
42 decoded data to the backend as it reads it. In Modbus' case, the state is
43 the ADU up to that point. This class represents the state and writes the
44 messages to the backend.
45 This class is for the common infrastructure between CS and SC. It should
46 not be used directly, only inhereted from.'''
48 def __init__(self, parent, start, write_channel, annotation_prefix):
49 self.data = [] # List of all the data received up to now
50 self.parent = parent # Reference to the decoder object
52 self.last_read = start # The last moment parsed by this ADU object
53 self.write_channel = write_channel
54 self.last_byte_put = -1
55 self.annotation_prefix = annotation_prefix
56 # Any Modbus message needs to be at least 4 bytes long. The Modbus
57 # function may make this longer.
58 self.minimum_length = 4
60 # This variable is used by an external function to determine when the
61 # next frame should be started.
62 self.startNewFrame = False
64 # If there is an error in a frame, we'd like to highlight it. Keep
68 def add_data(self, start, end, data):
69 '''Let the frame handle another piece of data.
70 start: start of this data
72 data: data as received from the UART decoder'''
73 ptype, rxtx, pdata = data
76 self.data.append(Data(start, end, pdata[0]))
77 self.parse() # parse() is defined in the specific type of ADU.
79 def puti(self, byte_to_put, annotation, message):
80 '''This class keeps track of how much of the data has already been
81 annotated. This function tells the parent class to write message, but
82 only if it hasn't written about this bit before.
83 byte_to_put: Only write if it hasn't yet written byte_to_put. It will
84 write from the start of self.last_byte_put+1 to the end
86 annotation: Annotation to write to, without annotation_prefix.
87 message: Message to write.'''
88 if byte_to_put > len(self.data) - 1:
89 # If the byte_to_put hasn't been read yet.
92 if annotation == 'error':
95 if byte_to_put > self.last_byte_put:
97 self.data[self.last_byte_put + 1].start,
98 self.data[byte_to_put].end,
99 self.annotation_prefix + annotation,
101 self.last_byte_put = byte_to_put
104 def putl(self, annotation, message, maximum=None):
105 '''Puts the last byte on the stack with message. The contents of the
106 last byte will be applied to message using format.'''
107 last_byte_address = len(self.data) - 1
108 if maximum is not None and last_byte_address > maximum:
110 self.puti(last_byte_address, annotation,
111 message.format(self.data[-1].data))
113 def close(self, message_overflow):
114 '''Function to be called when next message is started. As there is
115 always space between one message and the next, we can use that space
116 for errors at the end.'''
117 # TODO: Figure out how to make this happen for last message.
119 if len(data) < self.minimum_length:
121 # Sometimes happens with noise, safe to ignore.
124 data[self.last_byte_put].end, message_overflow,
125 self.annotation_prefix + 'error',
126 'Message too short or not finished')
128 if self.hasError and self.parent.options['scchannel'] != self.parent.options['cschannel']:
129 # If we are decoding different channels (so client->server and
130 # server->client messages can be separated) we like to mark blocks
131 # containing errors. We don't do this when decoding the same
132 # channel as both a client->server and server->client frame, and
133 # one of those is bound to contain an error, making highlighting
135 self.parent.puta(data[0].start, data[-1].end,
136 'error-indication', 'Frame contains error')
139 self.puti(len(data) - 1, 'error',
140 'Modbus data frames are limited to 256 bytes')
144 def check_crc(self, byte_to_put):
145 '''Check the CRC code, data[byte_to_put] is the 2nd byte of the CRC.'''
146 crc_byte1, crc_byte2 = self.calc_crc(byte_to_put)
148 if data[-2].data == crc_byte1 and data[-1].data == crc_byte2:
149 self.puti(byte_to_put, 'crc', 'CRC correct')
151 self.puti(byte_to_put, 'error',
152 'CRC should be {} {}'.format(crc_byte1, crc_byte2))
154 def half_word(self, start):
155 '''Return the half word (16 bit) value starting at start bytes in. If
156 it goes out of range it raises the usual errors.'''
157 if (start + 1) > (len(self.data) - 1):
158 # If there isn't enough length to access data[start + 1].
160 return self.data[start].data * 0x100 + self.data[start + 1].data
162 def calc_crc(self, last_byte):
163 '''Calculate the CRC, as described in the spec.
164 The last byte of the CRC should be data[last_byte].'''
166 # Every Modbus ADU should be as least 4 long, so we should never
167 # have to calculate a CRC on something shorter.
168 raise Exception('Could not calculate CRC: message too short')
171 magic_number = 0xA001 # As defined in the modbus specification.
172 for byte in self.data[:last_byte - 1]:
173 result = result ^ byte.data
177 if (LSB): # If the LSB is true.
178 result = result ^ magic_number
179 byte1 = result & 0xFF
180 byte2 = (result & 0xFF00) >> 8
181 return (byte1, byte2)
183 def parse_write_single_coil(self):
184 '''Parse function 5, write single coil.'''
185 self.minimum_length = 8
187 self.puti(1, 'function', 'Function 5: Write Single Coil')
189 address = self.half_word(2)
190 self.puti(3, 'address',
191 'Address 0x{:X} / {:d}'.format(address, address + 10000))
193 raw_value = self.half_word(4)
194 value = 'Invalid Coil Value'
195 if raw_value == 0x0000:
196 value = 'Coil Value OFF'
197 elif raw_value == 0xFF00:
198 value = 'Coil Value ON'
199 self.puti(5, 'data', value)
203 def parse_write_single_register(self):
204 '''Parse function 6, write single register.'''
205 self.minimum_length = 8
207 self.puti(1, 'function', 'Function 6: Write Single Register')
209 address = self.half_word(2)
210 self.puti(3, 'address',
211 'Address 0x{:X} / {:d}'.format(address, address + 30000))
213 value = self.half_word(4)
214 value_formatted = 'Register Value 0x{0:X} / {0:d}'.format(value)
215 self.puti(5, 'data', value_formatted)
219 def parse_diagnostics(self):
220 '''Parse function 8, diagnostics. This function has many subfunctions,
221 but they are all more or less the same.'''
222 self.minimum_length = 8
224 self.puti(1, 'function', 'Function 8: Diagnostics')
227 0: 'Return Query data',
228 1: 'Restart Communications Option',
229 2: 'Return Diagnostics Register',
230 3: 'Change ASCII Input Delimiter',
231 4: 'Force Listen Only Mode',
232 10: 'Clear Counters and Diagnostic Register',
233 11: 'Return Bus Message Count',
234 12: 'Return Bus Communication Error Count',
235 13: 'Return Bus Exception Error Count',
236 14: 'Return Slave Message Count',
237 15: 'Return Slave No Response Count',
238 16: 'Return Slave NAK Count',
239 17: 'Return Slave Busy Count',
240 18: 'Return Bus Character Overrun Count',
241 20: 'Return Overrun Counter and Flag',
243 subfunction = self.half_word(2)
244 subfunction_name = diag_subfunction.get(subfunction,
245 'Reserved subfunction')
247 'Subfunction {}: {}'.format(subfunction, subfunction_name))
249 diagnostic_data = self.half_word(4)
251 'Data Field: {0} / 0x{0:04X}'.format(diagnostic_data))
255 def parse_mask_write_register(self):
256 '''Parse function 22, Mask Write Register.'''
257 self.minimum_length = 10
260 self.puti(1, 'function', 'Function 22: Mask Write Register')
262 address = self.half_word(2)
263 self.puti(3, 'address',
264 'Address 0x{:X} / {:d}'.format(address, address + 30001))
266 self.half_word(4) # To make sure we don't oveflow data.
267 and_mask_1 = data[4].data
268 and_mask_2 = data[5].data
270 'AND mask: {:08b} {:08b}'.format(and_mask_1, and_mask_2))
272 self.half_word(6) # To make sure we don't oveflow data.
273 or_mask_1 = data[6].data
274 or_mask_2 = data[7].data
276 'OR mask: {:08b} {:08b}'.format(or_mask_1, or_mask_2))
280 def parse_not_implemented(self):
281 '''Explicitly mark certain functions as legal functions, but not
282 implemented in this parser. This is due to the author not being able to
283 find anything (hardware or software) that supports these functions.'''
284 # TODO: Implement these functions.
286 # Mentioning what function it is is no problem.
287 function = self.data[1].data
289 20: 'Read File Record',
290 21: 'Write File Record',
291 24: 'Read FIFO Queue',
292 43: 'Read Device Identification/Encapsulated Interface Transport',
294 self.puti(1, 'function',
295 'Function {}: {} (not supported)'.format(function, functionname))
297 # From there on out we can keep marking it unsupported.
298 self.putl('data', 'This function is not currently supported')
300 class Modbus_ADU_SC(Modbus_ADU):
301 '''SC stands for Server -> Client.'''
303 '''Select which specific Modbus function we should parse.'''
306 # This try-catch is being used as flow control.
308 server_id = data[0].data
309 if 1 <= server_id <= 247:
310 message = 'Slave ID: {}'.format(server_id)
312 message = 'Slave ID {} is invalid'
313 self.puti(0, 'server-id', message)
315 function = data[1].data
316 if function == 1 or function == 2:
317 self.parse_read_bits()
318 elif function == 3 or function == 4 or function == 23:
319 self.parse_read_registers()
321 self.parse_write_single_coil()
323 self.parse_write_single_register()
325 self.parse_read_exception_status()
327 self.parse_diagnostics()
329 self.parse_get_comm_event_counter()
331 self.parse_get_comm_event_log()
332 elif function == 15 or function == 16:
333 self.parse_write_multiple()
335 self.parse_report_server_id()
337 self.parse_mask_write_register()
338 elif function in {21, 21, 24, 43}:
339 self.parse_not_implemented()
340 elif function > 0x80:
343 self.puti(1, 'error',
344 'Unknown function: {}'.format(data[1].data))
345 self.putl('error', 'Unknown function')
347 # If the message gets here without raising an exception, the
348 # message goes on longer than it should.
349 self.putl('error', 'Message too long')
352 # Just a message saying we don't need to parse anymore this round.
355 def parse_read_bits(self):
356 self.mimumum_length = 5
359 function = data[1].data
362 self.puti(1, 'function', 'Function 1: Read Coils')
364 self.puti(1, 'function', 'Function 2: Read Discrete Inputs')
366 bytecount = self.data[2].data
367 self.minimum_length = 5 + bytecount # 3 before data, 2 CRC.
368 self.puti(2, 'length', 'Byte count: {}'.format(bytecount))
370 # From here on out, we expect registers on 3 and 4, 5 and 6 etc.
371 # So registers never start when the length is even.
372 self.putl('data', '{:08b}', bytecount + 2)
373 self.check_crc(bytecount + 4)
375 def parse_read_registers(self):
376 self.mimumum_length = 5
380 function = data[1].data
382 self.puti(1, 'function', 'Function 3: Read Holding Registers')
384 self.puti(1, 'function', 'Function 4: Read Input Registers')
386 self.puti(1, 'function', 'Function 23: Read/Write Multiple Registers')
388 bytecount = self.data[2].data
389 self.minimum_length = 5 + bytecount # 3 before data, 2 CRC.
390 if bytecount % 2 == 0:
391 self.puti(2, 'length', 'Byte count: {}'.format(bytecount))
393 self.puti(2, 'error',
394 'Error: Odd byte count ({})'.format(bytecount))
396 # From here on out, we expect registers on 3 and 4, 5 and 6 etc.
397 # So registers never start when the length is even.
398 if len(data) % 2 == 1:
399 register_value = self.half_word(-2)
400 self.putl('data', '0x{0:04X} / {0}'.format(register_value),
405 self.check_crc(bytecount + 4)
407 def parse_read_exception_status(self):
408 self.mimumum_length = 5
410 self.puti(1, 'function', 'Function 7: Read Exception Status')
411 exception_status = self.data[2].data
413 'Exception status: {:08b}'.format(exception_status))
416 def parse_get_comm_event_counter(self):
417 self.mimumum_length = 8
419 self.puti(1, 'function', 'Function 11: Get Comm Event Counter')
421 status = self.half_word(2)
423 self.puti(3, 'data', 'Status: not busy')
424 elif status == 0xFFFF:
425 self.puti(3, 'data', 'Status: busy')
427 self.puti(3, 'error', 'Bad status: 0x{:04X}'.format(status))
429 count = self.half_word(4)
430 self.puti(5, 'data', 'Event Count: {}'.format(count))
433 def parse_get_comm_event_log(self):
434 self.mimumum_length = 11
435 self.puti(1, 'function', 'Function 12: Get Comm Event Log')
439 bytecount = data[2].data
440 self.puti(2, 'length', 'Bytecount: {}'.format(bytecount))
441 # The bytecount is the length of everything except the slaveID,
442 # function code, bytecount and CRC.
443 self.mimumum_length = 5 + bytecount
445 status = self.half_word(3)
447 self.puti(4, 'data', 'Status: not busy')
448 elif status == 0xFFFF:
449 self.puti(4, 'data', 'Status: busy')
451 self.puti(4, 'error', 'Bad status: 0x{:04X}'.format(status))
453 event_count = self.half_word(5)
454 self.puti(6, 'data', 'Event Count: {}'.format(event_count))
456 message_count = self.half_word(7)
457 self.puti(8, 'data', 'Message Count: {}'.format(message_count))
459 self.putl('data', 'Event: 0x{:02X}'.format(data[-1].data),
462 self.check_crc(bytecount + 4)
464 def parse_write_multiple(self):
465 '''Function 15 and 16 are almost the same, so we can parse them both
466 using one function.'''
467 self.mimumum_length = 8
469 function = self.data[1].data
473 long_address_offset = 10001
475 data_unit = 'Registers'
477 long_address_offset = 30001
479 self.puti(1, 'function',
480 'Function {}: Write Multiple {}'.format(function, data_unit))
482 starting_address = self.half_word(2)
483 # Some instruction manuals use a long form name for addresses, this is
484 # listed here for convienience.
485 address_name = long_address_offset + starting_address
486 self.puti(3, 'address',
487 'Start at address 0x{:X} / {:d}'.format(starting_address,
490 quantity_of_outputs = self.half_word(4)
491 if quantity_of_outputs <= max_outputs:
493 'Write {} {}'.format(quantity_of_outputs, data_unit))
495 self.puti(5, 'error',
496 'Bad value: {} {}. Max is {}'.format(quantity_of_outputs,
497 data_unit, max_outputs))
501 def parse_report_server_id(self):
502 # Buildup of this function:
504 # 1 byte function (17)
506 # 1 byte serverID (counts for bytecount)
507 # 1 byte Run Indicator Status (counts for bytecount)
508 # bytecount - 2 bytes of device specific data (counts for bytecount)
510 self.mimumum_length = 7
512 self.puti(1, 'function', 'Function 17: Report Server ID')
514 bytecount = data[2].data
515 self.puti(2, 'length', 'Data is {} bytes long'.format(bytecount))
517 self.puti(3, 'data', 'serverID: {}'.format(data[3].data))
519 run_indicator_status = data[4].data
520 if run_indicator_status == 0x00:
521 self.puti(4, 'data', 'Run Indicator status: Off')
522 elif run_indicator_status == 0xFF:
523 self.puti(4, 'data', 'Run Indicator status: On')
525 self.puti(4, 'error',
526 'Bad Run Indicator status: 0x{:X}'.format(run_indicator_status))
528 self.putl('data', 'Device specific data: {}, "{}"'.format(data[-1].data,
529 chr(data[-1].data)), 2 + bytecount)
531 self.check_crc(4 + bytecount)
533 def parse_error(self):
534 '''Parse a Modbus error message.'''
535 self.mimumum_length = 5
536 # The function code of an error is always 0x80 above the function call
538 functioncode = self.data[1].data - 0x80
542 2: 'Read Discrete Inputs',
543 3: 'Read Holding Registers',
544 4: 'Read Input Registers',
545 5: 'Write Single Coil',
546 6: 'Write Single Register',
547 7: 'Read Exception Status',
549 11: 'Get Com Event Counter',
550 12: 'Get Com Event Log',
551 15: 'Write Multiple Coils',
552 16: 'Write Multiple Registers',
553 17: 'Report Slave ID',
554 20: 'Read File Record',
555 21: 'Write File Record',
556 22: 'Mask Write Register',
557 23: 'Read/Write Multiple Registers',
558 24: 'Read FIFO Queue',
559 43: 'Read Device Identification/Encapsulated Interface Transport',
561 functionname = '{}: {}'.format(functioncode,
562 functions.get(functioncode, 'Unknown function'))
563 self.puti(1, 'function',
564 'Error for function {}'.format(functionname))
566 error = self.data[2].data
568 1: 'Illegal Function',
569 2: 'Illegal Data Address',
570 3: 'Illegal Data Value',
571 4: 'Slave Device Failure',
573 6: 'Slave Device Busy',
574 8: 'Memory Parity Error',
575 10: 'Gateway Path Unavailable',
576 11: 'Gateway Target Device failed to respond',
578 errorname = '{}: {}'.format(error, errorcodes.get(error, 'Unknown'))
579 self.puti(2, 'data', 'Error {}'.format(errorname))
582 class Modbus_ADU_CS(Modbus_ADU):
583 '''CS stands for Client -> Server.'''
585 '''Select which specific Modbus function we should parse.'''
588 # This try-catch is being used as flow control.
590 server_id = data[0].data
593 message = 'Broadcast message'
594 elif 1 <= server_id <= 247:
595 message = 'Slave ID: {}'.format(server_id)
596 elif 248 <= server_id <= 255:
597 message = 'Slave ID: {} (reserved address)'.format(server_id)
598 self.puti(0, 'server-id', message)
600 function = data[1].data
601 if function >= 1 and function <= 4:
602 self.parse_read_data_command()
604 self.parse_write_single_coil()
606 self.parse_write_single_register()
607 if function in {7, 11, 12, 17}:
608 self.parse_single_byte_request()
610 self.parse_diagnostics()
611 if function in {15, 16}:
612 self.parse_write_multiple()
614 self.parse_mask_write_register()
616 self.parse_read_write_registers()
617 elif function in {21, 21, 24, 43}:
618 self.parse_not_implemented()
620 self.puti(1, 'error',
621 'Unknown function: {}'.format(data[1].data))
622 self.putl('error', 'Unknown function')
624 # If the message gets here without raising an exception, the
625 # message goes on longer than it should.
626 self.putl('error', 'Message too long')
629 # Just a message saying we don't need to parse anymore this round.
632 def parse_read_data_command(self):
633 '''Interpret a command to read x units of data starting at address, ie
634 functions 1, 2, 3 and 4, and write the result to the annotations.'''
636 self.minimum_length = 8
638 function = data[1].data
639 functionname = {1: 'Read Coils',
640 2: 'Read Discrete Inputs',
641 3: 'Read Holding Registers',
642 4: 'Read Input Registers',
645 self.puti(1, 'function',
646 'Function {}: {}'.format(function, functionname))
648 starting_address = self.half_word(2)
649 # Some instruction manuals use a long form name for addresses, this is
650 # listed here for convienience.
651 # Example: holding register 60 becomes 30061.
652 address_name = 10000 * function + 1 + starting_address
653 self.puti(3, 'address',
654 'Start at address 0x{:X} / {:d}'.format(starting_address,
657 self.puti(5, 'length',
658 'Read {:d} units of data'.format(self.half_word(4)))
661 def parse_single_byte_request(self):
662 '''Some Modbus functions have no arguments, this parses those.'''
663 function = self.data[1].data
664 function_name = {7: 'Read Exception Status',
665 11: 'Get Comm Event Counter',
666 12: 'Get Comm Event Log',
667 17: 'Report Slave ID',
669 self.puti(1, 'function',
670 'Function {}: {}'.format(function, function_name))
674 def parse_write_multiple(self):
675 '''Function 15 and 16 are almost the same, so we can parse them both
676 using one function.'''
677 self.mimumum_length = 9
679 function = self.data[1].data
683 ratio_bytes_data = 1/8
684 long_address_offset = 10001
686 data_unit = 'Registers'
689 long_address_offset = 30001
691 self.puti(1, 'function',
692 'Function {}: Write Multiple {}'.format(function, data_unit))
694 starting_address = self.half_word(2)
695 # Some instruction manuals use a long form name for addresses, this is
696 # listed here for convienience.
697 address_name = long_address_offset + starting_address
698 self.puti(3, 'address',
699 'Start at address 0x{:X} / {:d}'.format(starting_address,
702 quantity_of_outputs = self.half_word(4)
703 if quantity_of_outputs <= max_outputs:
704 self.puti(5, 'length',
705 'Write {} {}'.format(quantity_of_outputs, data_unit))
707 self.puti(5, 'error',
708 'Bad value: {} {}. Max is {}'.format(quantity_of_outputs,
709 data_unit, max_outputs))
710 proper_bytecount = ceil(quantity_of_outputs * ratio_bytes_data)
712 bytecount = self.data[6].data
713 if bytecount == proper_bytecount:
714 self.puti(6, 'length', 'Byte count: {}'.format(bytecount))
716 self.puti(6, 'error',
717 'Bad byte count, is {}, should be {}'.format(bytecount,
719 self.mimumum_length = bytecount + 9
721 self.putl('data', 'Value 0x{:X}', 6 + bytecount)
723 self.check_crc(bytecount + 8)
725 def parse_read_file_record(self):
726 self.puti(1, 'function', 'Function 20: Read file records')
730 bytecount = data[2].data
732 self.minimum_length = 5 + bytecount
733 # 1 for serverID, 1 for function, 1 for bytecount, 2 for CRC.
735 if 0x07 <= bytecount <= 0xF5:
736 self.puti(2, 'length', 'Request is {} bytes long'.format(bytecount))
738 self.puti(2, 'error',
739 'Request claims to be {} bytes long, legal values are between'
740 ' 7 and 247'.format(bytecount))
742 current_byte = len(data) - 1
743 # Function 20 is a number of sub-requests, the first starting at 3,
744 # the total length of the sub-requests is bytecount.
745 if current_byte <= bytecount + 2:
746 step = (current_byte - 3) % 7
748 if data[current_byte].data == 6:
749 self.puti(current_byte, 'data', 'Start sub-request')
751 self.puti(current_byte, 'error',
752 'First byte of subrequest should be 0x06')
756 file_number = self.half_word(current_byte - 1)
757 self.puti(current_byte, 'data',
758 'Read File number {}'.format(file_number))
762 record_number = self.half_word(current_byte - 1)
763 self.puti(current_byte, 'address',
764 'Read from record number {}'.format(record_number))
765 # TODO: Check if within range.
769 records_to_read = self.half_word(current_byte - 1)
770 self.puti(current_byte, 'length',
771 'Read {} records'.format(records_to_read))
772 self.check_crc(4 + bytecount)
774 def parse_read_write_registers(self):
775 '''Parse function 23: Read/Write multiple registers.'''
776 self.minimum_length = 13
778 self.puti(1, 'function', 'Function 23: Read/Write Multiple Registers')
780 starting_address = self.half_word(2)
781 # Some instruction manuals use a long form name for addresses, this is
782 # listed here for convienience.
783 # Example: holding register 60 becomes 30061.
784 address_name = 30001 + starting_address
785 self.puti(3, 'address',
786 'Read starting at address 0x{:X} / {:d}'.format(starting_address,
789 self.puti(5, 'length', 'Read {:d} units of data'.format(self.half_word(4)))
791 starting_address = self.half_word(6)
792 self.puti(7, 'address',
793 'Write starting at address 0x{:X} / {:d}'.format(starting_address,
796 quantity_of_outputs = self.half_word(8)
797 self.puti(9, 'length',
798 'Write {} registers'.format(quantity_of_outputs))
799 proper_bytecount = quantity_of_outputs * 2
801 bytecount = self.data[10].data
802 if bytecount == proper_bytecount:
803 self.puti(10, 'length', 'Byte count: {}'.format(bytecount))
805 self.puti(10, 'error',
806 'Bad byte count, is {}, should be {}'.format(bytecount,
808 self.mimumum_length = bytecount + 13
810 self.putl('data', 'Data, value 0x{:02X}', 10 + bytecount)
812 self.check_crc(bytecount + 12)
814 class Decoder(srd.Decoder):
818 longname = 'Modbus RTU over RS232/RS485'
819 desc = 'Modbus RTU protocol for industrial applications.'
823 tags = ['Embedded/industrial']
825 ('sc-server-id', ''),
832 ('cs-server-id', ''),
839 ('error-indication', ''),
842 ('sc', 'Server->client', (0, 1, 2, 3, 4, 5, 6)),
843 ('cs', 'Client->server', (7, 8, 9, 10, 11, 12, 13)),
844 ('error-indicator', 'Errors in frame', (14,)),
847 {'id': 'scchannel', 'desc': 'Server -> client channel',
848 'default': rxtx_channels[0], 'values': rxtx_channels},
849 {'id': 'cschannel', 'desc': 'Client -> server channel',
850 'default': rxtx_channels[1], 'values': rxtx_channels},
851 {'id': 'framegap', 'desc': 'Inter-frame bit gap', 'default': 28},
858 self.ADUSc = None # Start off with empty slave -> client ADU.
859 self.ADUCs = None # Start off with empty client -> slave ADU.
861 # The reason we have both (despite not supporting full duplex comms) is
862 # because we want to be able to decode the message as both client ->
863 # server and server -> client, and let the user see which of the two
866 self.bitlength = None # We will later test how long a bit is.
869 self.out_ann = self.register(srd.OUTPUT_ANN)
871 def puta(self, start, end, ann_str, message):
872 '''Put an annotation from start to end, with ann as a
873 string. This means you don't have to know the ann's
874 number to write annotations to it.'''
875 ann = [s[0] for s in self.annotations].index(ann_str)
876 self.put(start, end, self.out_ann, [ann, [message]])
878 def decode_adu(self, ss, es, data, direction):
879 '''Decode the next byte or bit (depending on type) in the ADU.
880 ss: Start time of the data
881 es: End time of the data
882 data: Data as passed from the UART decoder
883 direction: Is this data for the Cs (client -> server) or Sc (server ->
884 client) being decoded right now?'''
885 ptype, rxtx, pdata = data
887 # We don't have a nice way to get the baud rate from UART, so we have
888 # to figure out how long a bit lasts. We do this by looking at the
889 # length of (probably) the startbit.
890 if self.bitlength is None:
891 if ptype == 'STARTBIT' or ptype == 'STOPBIT':
892 self.bitlength = es - ss
894 # If we don't know the bitlength yet, we can't start decoding.
897 # Select the ADU, create the ADU if needed.
898 # We set ADU.startNewFrame = True when we know the old one is over.
899 if direction == 'Sc':
900 if (self.ADUSc is None) or self.ADUSc.startNewFrame:
901 self.ADUSc = Modbus_ADU_SC(self, ss, TX, 'sc-')
903 if direction == 'Cs':
904 if self.ADUCs is None or self.ADUCs.startNewFrame:
905 self.ADUCs = Modbus_ADU_CS(self, ss, TX, 'cs-')
908 # We need to determine if the last ADU is over.
909 # According to the Modbus spec, there should be 3.5 characters worth of
910 # space between each message. But if within a message there is a length
911 # of more than 1.5 character, that's an error. For our purposes
912 # somewhere between seems fine.
913 # A character is 11 bits long, so (3.5 + 1.5)/2 * 11 ~= 28
914 # TODO: Display error for too short or too long.
915 if (ss - ADU.last_read) <= self.bitlength * self.options['framegap']:
916 ADU.add_data(ss, es, data)
918 # It's been too long since the last part of the ADU!
919 # If there is any data in the ADU we need to show it to the user
920 if len(ADU.data) > 0:
921 # Extend errors for 3 bits after last byte, we can guarantee
923 ADU.close(ADU.data[-1].end + self.bitlength * 3)
925 ADU.startNewFrame = True
926 # Restart this function, it will make a new ADU for us.
927 self.decode_adu(ss, es, data, direction)
929 def decode(self, ss, es, data):
930 ptype, rxtx, pdata = data
932 # Ignore unknown/unsupported ptypes.
933 if ptype not in ('STARTBIT', 'DATA', 'STOPBIT'):
936 # Decide what ADU(s) we need this packet to go to.
937 # Note that it's possible to go to both ADUs.
938 if rxtx_channels[rxtx] == self.options['scchannel']:
939 self.decode_adu(ss, es, data, 'Sc')
940 if rxtx_channels[rxtx] == self.options['cschannel']:
941 self.decode_adu(ss, es, data, 'Cs')