modbus-crc 1.2__tar.gz → 2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modbus_crc-2.0/PKG-INFO +185 -0
- modbus_crc-2.0/README.md +160 -0
- modbus_crc-2.0/modbus_crc/__init__.py +55 -0
- modbus_crc-2.0/modbus_crc/py.typed +0 -0
- modbus_crc-2.0/modbus_crc.egg-info/PKG-INFO +185 -0
- {modbus-crc-1.2 → modbus_crc-2.0}/modbus_crc.egg-info/SOURCES.txt +5 -4
- {modbus-crc-1.2 → modbus_crc-2.0}/modbus_crc.egg-info/not-zip-safe +1 -1
- modbus_crc-2.0/modbus_crc.egg-info/requires.txt +3 -0
- modbus_crc-2.0/pyproject.toml +49 -0
- modbus_crc-2.0/setup.cfg +4 -0
- modbus_crc-2.0/tests/test_modbus_crc.py +80 -0
- modbus-crc-1.2/MANIFEST.in +0 -2
- modbus-crc-1.2/PKG-INFO +0 -70
- modbus-crc-1.2/README.md +0 -50
- modbus-crc-1.2/modbus_crc/__init__.py +0 -69
- modbus-crc-1.2/modbus_crc.egg-info/PKG-INFO +0 -70
- modbus-crc-1.2/setup.cfg +0 -8
- modbus-crc-1.2/setup.py +0 -24
- {modbus-crc-1.2 → modbus_crc-2.0}/LICENSE.md +0 -0
- {modbus-crc-1.2 → modbus_crc-2.0}/modbus_crc.egg-info/dependency_links.txt +0 -0
- {modbus-crc-1.2 → modbus_crc-2.0}/modbus_crc.egg-info/top_level.txt +0 -0
modbus_crc-2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modbus-crc
|
|
3
|
+
Version: 2.0
|
|
4
|
+
Summary: CRC-16 calculation for Modbus protocol
|
|
5
|
+
Author-email: Alexey Kuznetsov <webtoucher@ya.ru>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/webtoucher/modbus-crc
|
|
8
|
+
Project-URL: Repository, https://github.com/webtoucher/modbus-crc
|
|
9
|
+
Project-URL: Issues, https://github.com/webtoucher/modbus-crc/issues
|
|
10
|
+
Keywords: modbus,crc,crc16,checksum,serial,rtu
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX
|
|
16
|
+
Classifier: Operating System :: Unix
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE.md
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Modbus CRC-16
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
[](https://pypi.python.org/pypi/modbus-crc)
|
|
30
|
+
[](https://pypi.python.org/pypi/modbus-crc)
|
|
31
|
+
[](https://pypi.python.org/pypi/modbus-crc)
|
|
32
|
+
[](https://github.com/webtoucher/modbus-crc/actions/workflows/tests.yml)
|
|
33
|
+
|
|
34
|
+
CRC-16 calculation library for Modbus protocol. Tiny, dependency-free and fast: a single
|
|
35
|
+
256-entry lookup table, no runtime dependencies, type hints included.
|
|
36
|
+
|
|
37
|
+
## Algorithm
|
|
38
|
+
|
|
39
|
+
The checksum is **CRC-16/MODBUS**:
|
|
40
|
+
|
|
41
|
+
| Parameter | Value |
|
|
42
|
+
|-----------------------|-------------------------------------------|
|
|
43
|
+
| Width | 16 bits |
|
|
44
|
+
| Polynomial | `0x8005` (reflected: `0xA001`) |
|
|
45
|
+
| Initial value | `0xFFFF` |
|
|
46
|
+
| Reflect in / out | yes / yes |
|
|
47
|
+
| Final XOR | `0x0000` |
|
|
48
|
+
| Check (`"123456789"`) | `0x4B37`, transmitted as `b'\x37\x4B'` |
|
|
49
|
+
| Byte order on wire | low byte first |
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
Install it with pip:
|
|
54
|
+
|
|
55
|
+
```shell
|
|
56
|
+
$ pip install modbus-crc
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Or you can add it as dependency in requirements.txt file of your python application:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
modbus-crc~=2.0
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
For signing byte package use `add_crc`:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from modbus_crc import add_crc
|
|
71
|
+
|
|
72
|
+
signed_package = add_crc(b'\x00\x12\x34\xAB\xCD\xEF')
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Result is `b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'`. For validation signed package use `check_crc`:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from modbus_crc import check_crc
|
|
79
|
+
|
|
80
|
+
if not check_crc(b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'):
|
|
81
|
+
raise SomeException()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
To get the checksum alone (low byte first, exactly as it is transmitted on the wire), use `crc16`:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from modbus_crc import crc16
|
|
88
|
+
|
|
89
|
+
crc16(b'\x01\x03\x00\x00\x00\x0A') # b'\xC5\xCD'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Here is a real Modbus RTU request — "read 10 holding registers from slave 1":
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
add_crc(b'\x01\x03\x00\x00\x00\x0A') # b'\x01\x03\x00\x00\x00\x0A\xC5\xCD'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`check_crc` validates the whole signed frame, so it works with data read straight from
|
|
99
|
+
the serial port, without splitting off the trailing bytes:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
check_crc(b'\x01\x03\x00\x00\x00\x0A\xC5\xCD') # True
|
|
103
|
+
check_crc(b'\x01\x03\x00\x00\x00\x0A\xC5\xCE') # False
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## API
|
|
107
|
+
|
|
108
|
+
All three functions accept `bytes`, `bytearray` and `memoryview` (declared as `BytesLike`).
|
|
109
|
+
|
|
110
|
+
| Function | Returns | Description |
|
|
111
|
+
|---------------------------------|---------|-----------------------------------------------------------------|
|
|
112
|
+
| `crc16(data)` | `bytes` | 2-byte checksum of `data`, low byte first |
|
|
113
|
+
| `add_crc(package)` | `bytes` | `package` with the checksum appended; input type is normalized |
|
|
114
|
+
| `check_crc(package)` | `bool` | `True` if `package` carries a valid trailing checksum |
|
|
115
|
+
|
|
116
|
+
Notes:
|
|
117
|
+
|
|
118
|
+
- `add_crc` always returns `bytes`, even for `bytearray` and `memoryview` input.
|
|
119
|
+
- `check_crc(b'')` is `False`: an empty package has no valid checksum.
|
|
120
|
+
- Functions are pure: they keep no state between calls. The lookup table is built once,
|
|
121
|
+
at import time.
|
|
122
|
+
|
|
123
|
+
## Compatibility
|
|
124
|
+
|
|
125
|
+
- Python **3.9 and newer**, including free-threaded builds. Tested on 3.9 and 3.15 and
|
|
126
|
+
in CI on every version from 3.9 to 3.15.
|
|
127
|
+
- No runtime dependencies.
|
|
128
|
+
- Type hints are shipped with the package (`py.typed`, PEP 561), so mypy and pyright
|
|
129
|
+
see real signatures instead of `Any`.
|
|
130
|
+
|
|
131
|
+
## Development
|
|
132
|
+
|
|
133
|
+
```shell
|
|
134
|
+
$ python -m venv .venv
|
|
135
|
+
$ .venv\Scripts\activate # Windows; on POSIX: source .venv/bin/activate
|
|
136
|
+
$ pip install -e ".[dev]" # installs the package and pytest
|
|
137
|
+
$ python -m pytest
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The test suite covers:
|
|
141
|
+
|
|
142
|
+
- canonical Modbus frames with published checksums,
|
|
143
|
+
- an independent bit-by-bit implementation written from the specification, used as an
|
|
144
|
+
oracle against the fast table-driven code (including random payloads up to 1 KiB),
|
|
145
|
+
- edge cases: empty package, one-byte packages, `bytearray` / `memoryview` input,
|
|
146
|
+
- detection of every single-byte corruption of a signed frame.
|
|
147
|
+
|
|
148
|
+
Project layout:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
modbus_crc/__init__.py the whole library
|
|
152
|
+
tests/test_modbus_crc.py test suite
|
|
153
|
+
pyproject.toml metadata, build config and pytest settings
|
|
154
|
+
.github/workflows/ CI: tests on Python 3.9-3.15, sdist/wheel build check
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
To build and verify a release:
|
|
158
|
+
|
|
159
|
+
```shell
|
|
160
|
+
$ pip install build twine
|
|
161
|
+
$ python -m build
|
|
162
|
+
$ python -m twine check dist/*
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Migrating from 1.x
|
|
166
|
+
|
|
167
|
+
The public API is unchanged, so most projects need no code changes. What did change:
|
|
168
|
+
|
|
169
|
+
- **Python 3.9+ is required.** Python 2 support and the legacy `setup.py` / `setup.cfg`
|
|
170
|
+
are gone; metadata now lives in `pyproject.toml` (PEP 621).
|
|
171
|
+
- **`LOW_BYTES` and `HIGH_BYTES` were removed.** Those were two 256-byte tables with
|
|
172
|
+
inverted naming that leaked from the implementation. The lookup table is now generated
|
|
173
|
+
at import time and is private — use the documented functions instead.
|
|
174
|
+
- **`add_crc` always returns `bytes`.** Previously `add_crc(bytearray(...))` returned a
|
|
175
|
+
`bytearray`; the result type is now stable regardless of the input type.
|
|
176
|
+
- **`modbus_crc.__version__` is available** and is the single source of truth for the
|
|
177
|
+
package version.
|
|
178
|
+
- **The package ships `py.typed`**, so type checkers no longer treat it as untyped.
|
|
179
|
+
- **Faster and clearer internals:** one table instead of two, a single 16-bit register
|
|
180
|
+
instead of two byte variables, no `int()` call per byte (over 20% faster per KB on
|
|
181
|
+
CPython 3.11).
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
Distributed under the BSD 3-Clause license. See [LICENSE.md](LICENSE.md) for details.
|
modbus_crc-2.0/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Modbus CRC-16
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
[](https://pypi.python.org/pypi/modbus-crc)
|
|
5
|
+
[](https://pypi.python.org/pypi/modbus-crc)
|
|
6
|
+
[](https://pypi.python.org/pypi/modbus-crc)
|
|
7
|
+
[](https://github.com/webtoucher/modbus-crc/actions/workflows/tests.yml)
|
|
8
|
+
|
|
9
|
+
CRC-16 calculation library for Modbus protocol. Tiny, dependency-free and fast: a single
|
|
10
|
+
256-entry lookup table, no runtime dependencies, type hints included.
|
|
11
|
+
|
|
12
|
+
## Algorithm
|
|
13
|
+
|
|
14
|
+
The checksum is **CRC-16/MODBUS**:
|
|
15
|
+
|
|
16
|
+
| Parameter | Value |
|
|
17
|
+
|-----------------------|-------------------------------------------|
|
|
18
|
+
| Width | 16 bits |
|
|
19
|
+
| Polynomial | `0x8005` (reflected: `0xA001`) |
|
|
20
|
+
| Initial value | `0xFFFF` |
|
|
21
|
+
| Reflect in / out | yes / yes |
|
|
22
|
+
| Final XOR | `0x0000` |
|
|
23
|
+
| Check (`"123456789"`) | `0x4B37`, transmitted as `b'\x37\x4B'` |
|
|
24
|
+
| Byte order on wire | low byte first |
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
Install it with pip:
|
|
29
|
+
|
|
30
|
+
```shell
|
|
31
|
+
$ pip install modbus-crc
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or you can add it as dependency in requirements.txt file of your python application:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
modbus-crc~=2.0
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
For signing byte package use `add_crc`:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from modbus_crc import add_crc
|
|
46
|
+
|
|
47
|
+
signed_package = add_crc(b'\x00\x12\x34\xAB\xCD\xEF')
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Result is `b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'`. For validation signed package use `check_crc`:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from modbus_crc import check_crc
|
|
54
|
+
|
|
55
|
+
if not check_crc(b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'):
|
|
56
|
+
raise SomeException()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
To get the checksum alone (low byte first, exactly as it is transmitted on the wire), use `crc16`:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from modbus_crc import crc16
|
|
63
|
+
|
|
64
|
+
crc16(b'\x01\x03\x00\x00\x00\x0A') # b'\xC5\xCD'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Here is a real Modbus RTU request — "read 10 holding registers from slave 1":
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
add_crc(b'\x01\x03\x00\x00\x00\x0A') # b'\x01\x03\x00\x00\x00\x0A\xC5\xCD'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`check_crc` validates the whole signed frame, so it works with data read straight from
|
|
74
|
+
the serial port, without splitting off the trailing bytes:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
check_crc(b'\x01\x03\x00\x00\x00\x0A\xC5\xCD') # True
|
|
78
|
+
check_crc(b'\x01\x03\x00\x00\x00\x0A\xC5\xCE') # False
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API
|
|
82
|
+
|
|
83
|
+
All three functions accept `bytes`, `bytearray` and `memoryview` (declared as `BytesLike`).
|
|
84
|
+
|
|
85
|
+
| Function | Returns | Description |
|
|
86
|
+
|---------------------------------|---------|-----------------------------------------------------------------|
|
|
87
|
+
| `crc16(data)` | `bytes` | 2-byte checksum of `data`, low byte first |
|
|
88
|
+
| `add_crc(package)` | `bytes` | `package` with the checksum appended; input type is normalized |
|
|
89
|
+
| `check_crc(package)` | `bool` | `True` if `package` carries a valid trailing checksum |
|
|
90
|
+
|
|
91
|
+
Notes:
|
|
92
|
+
|
|
93
|
+
- `add_crc` always returns `bytes`, even for `bytearray` and `memoryview` input.
|
|
94
|
+
- `check_crc(b'')` is `False`: an empty package has no valid checksum.
|
|
95
|
+
- Functions are pure: they keep no state between calls. The lookup table is built once,
|
|
96
|
+
at import time.
|
|
97
|
+
|
|
98
|
+
## Compatibility
|
|
99
|
+
|
|
100
|
+
- Python **3.9 and newer**, including free-threaded builds. Tested on 3.9 and 3.15 and
|
|
101
|
+
in CI on every version from 3.9 to 3.15.
|
|
102
|
+
- No runtime dependencies.
|
|
103
|
+
- Type hints are shipped with the package (`py.typed`, PEP 561), so mypy and pyright
|
|
104
|
+
see real signatures instead of `Any`.
|
|
105
|
+
|
|
106
|
+
## Development
|
|
107
|
+
|
|
108
|
+
```shell
|
|
109
|
+
$ python -m venv .venv
|
|
110
|
+
$ .venv\Scripts\activate # Windows; on POSIX: source .venv/bin/activate
|
|
111
|
+
$ pip install -e ".[dev]" # installs the package and pytest
|
|
112
|
+
$ python -m pytest
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The test suite covers:
|
|
116
|
+
|
|
117
|
+
- canonical Modbus frames with published checksums,
|
|
118
|
+
- an independent bit-by-bit implementation written from the specification, used as an
|
|
119
|
+
oracle against the fast table-driven code (including random payloads up to 1 KiB),
|
|
120
|
+
- edge cases: empty package, one-byte packages, `bytearray` / `memoryview` input,
|
|
121
|
+
- detection of every single-byte corruption of a signed frame.
|
|
122
|
+
|
|
123
|
+
Project layout:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
modbus_crc/__init__.py the whole library
|
|
127
|
+
tests/test_modbus_crc.py test suite
|
|
128
|
+
pyproject.toml metadata, build config and pytest settings
|
|
129
|
+
.github/workflows/ CI: tests on Python 3.9-3.15, sdist/wheel build check
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
To build and verify a release:
|
|
133
|
+
|
|
134
|
+
```shell
|
|
135
|
+
$ pip install build twine
|
|
136
|
+
$ python -m build
|
|
137
|
+
$ python -m twine check dist/*
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Migrating from 1.x
|
|
141
|
+
|
|
142
|
+
The public API is unchanged, so most projects need no code changes. What did change:
|
|
143
|
+
|
|
144
|
+
- **Python 3.9+ is required.** Python 2 support and the legacy `setup.py` / `setup.cfg`
|
|
145
|
+
are gone; metadata now lives in `pyproject.toml` (PEP 621).
|
|
146
|
+
- **`LOW_BYTES` and `HIGH_BYTES` were removed.** Those were two 256-byte tables with
|
|
147
|
+
inverted naming that leaked from the implementation. The lookup table is now generated
|
|
148
|
+
at import time and is private — use the documented functions instead.
|
|
149
|
+
- **`add_crc` always returns `bytes`.** Previously `add_crc(bytearray(...))` returned a
|
|
150
|
+
`bytearray`; the result type is now stable regardless of the input type.
|
|
151
|
+
- **`modbus_crc.__version__` is available** and is the single source of truth for the
|
|
152
|
+
package version.
|
|
153
|
+
- **The package ships `py.typed`**, so type checkers no longer treat it as untyped.
|
|
154
|
+
- **Faster and clearer internals:** one table instead of two, a single 16-bit register
|
|
155
|
+
instead of two byte variables, no `int()` call per byte (over 20% faster per KB on
|
|
156
|
+
CPython 3.11).
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
Distributed under the BSD 3-Clause license. See [LICENSE.md](LICENSE.md) for details.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CRC-16 calculation for the Modbus protocol.
|
|
3
|
+
|
|
4
|
+
The checksum is CRC-16/MODBUS: polynomial 0xA001 (reflected 0x8005),
|
|
5
|
+
initial value 0xFFFF, no final XOR, transmitted low byte first.
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 webtoucher
|
|
8
|
+
Distributed under the BSD 3-Clause license. See LICENSE for more info.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Union
|
|
12
|
+
|
|
13
|
+
__all__ = ["crc16", "add_crc", "check_crc"]
|
|
14
|
+
|
|
15
|
+
__version__ = "2.0"
|
|
16
|
+
|
|
17
|
+
BytesLike = Union[bytes, bytearray, memoryview]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _build_crc_table(polynomial: int = 0xA001) -> tuple:
|
|
21
|
+
"""Build the 256-entry lookup table for CRC-16/MODBUS."""
|
|
22
|
+
table = []
|
|
23
|
+
for index in range(256):
|
|
24
|
+
crc = index
|
|
25
|
+
for _ in range(8):
|
|
26
|
+
crc = (crc >> 1) ^ polynomial if crc & 1 else crc >> 1
|
|
27
|
+
table.append(crc)
|
|
28
|
+
return tuple(table)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_CRC_TABLE = _build_crc_table()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _crc_register(data: BytesLike) -> int:
|
|
35
|
+
"""Return the raw 16-bit CRC register value of data."""
|
|
36
|
+
crc = 0xFFFF
|
|
37
|
+
for byte in data:
|
|
38
|
+
crc = (crc >> 8) ^ _CRC_TABLE[(crc ^ byte) & 0xFF]
|
|
39
|
+
return crc
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def crc16(data: BytesLike) -> bytes:
|
|
43
|
+
"""Calculate CRC-16/MODBUS of data, low byte first as on the wire."""
|
|
44
|
+
crc = _crc_register(data)
|
|
45
|
+
return bytes((crc & 0xFF, crc >> 8))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def add_crc(package: BytesLike) -> bytes:
|
|
49
|
+
"""Return package with CRC-16/MODBUS appended."""
|
|
50
|
+
return bytes(package) + crc16(package)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_crc(package: BytesLike) -> bool:
|
|
54
|
+
"""Return True if package carries a valid trailing CRC-16/MODBUS."""
|
|
55
|
+
return _crc_register(package) == 0
|
|
File without changes
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modbus-crc
|
|
3
|
+
Version: 2.0
|
|
4
|
+
Summary: CRC-16 calculation for Modbus protocol
|
|
5
|
+
Author-email: Alexey Kuznetsov <webtoucher@ya.ru>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/webtoucher/modbus-crc
|
|
8
|
+
Project-URL: Repository, https://github.com/webtoucher/modbus-crc
|
|
9
|
+
Project-URL: Issues, https://github.com/webtoucher/modbus-crc/issues
|
|
10
|
+
Keywords: modbus,crc,crc16,checksum,serial,rtu
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX
|
|
16
|
+
Classifier: Operating System :: Unix
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE.md
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Modbus CRC-16
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
[](https://pypi.python.org/pypi/modbus-crc)
|
|
30
|
+
[](https://pypi.python.org/pypi/modbus-crc)
|
|
31
|
+
[](https://pypi.python.org/pypi/modbus-crc)
|
|
32
|
+
[](https://github.com/webtoucher/modbus-crc/actions/workflows/tests.yml)
|
|
33
|
+
|
|
34
|
+
CRC-16 calculation library for Modbus protocol. Tiny, dependency-free and fast: a single
|
|
35
|
+
256-entry lookup table, no runtime dependencies, type hints included.
|
|
36
|
+
|
|
37
|
+
## Algorithm
|
|
38
|
+
|
|
39
|
+
The checksum is **CRC-16/MODBUS**:
|
|
40
|
+
|
|
41
|
+
| Parameter | Value |
|
|
42
|
+
|-----------------------|-------------------------------------------|
|
|
43
|
+
| Width | 16 bits |
|
|
44
|
+
| Polynomial | `0x8005` (reflected: `0xA001`) |
|
|
45
|
+
| Initial value | `0xFFFF` |
|
|
46
|
+
| Reflect in / out | yes / yes |
|
|
47
|
+
| Final XOR | `0x0000` |
|
|
48
|
+
| Check (`"123456789"`) | `0x4B37`, transmitted as `b'\x37\x4B'` |
|
|
49
|
+
| Byte order on wire | low byte first |
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
Install it with pip:
|
|
54
|
+
|
|
55
|
+
```shell
|
|
56
|
+
$ pip install modbus-crc
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Or you can add it as dependency in requirements.txt file of your python application:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
modbus-crc~=2.0
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
For signing byte package use `add_crc`:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from modbus_crc import add_crc
|
|
71
|
+
|
|
72
|
+
signed_package = add_crc(b'\x00\x12\x34\xAB\xCD\xEF')
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Result is `b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'`. For validation signed package use `check_crc`:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from modbus_crc import check_crc
|
|
79
|
+
|
|
80
|
+
if not check_crc(b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'):
|
|
81
|
+
raise SomeException()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
To get the checksum alone (low byte first, exactly as it is transmitted on the wire), use `crc16`:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from modbus_crc import crc16
|
|
88
|
+
|
|
89
|
+
crc16(b'\x01\x03\x00\x00\x00\x0A') # b'\xC5\xCD'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Here is a real Modbus RTU request — "read 10 holding registers from slave 1":
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
add_crc(b'\x01\x03\x00\x00\x00\x0A') # b'\x01\x03\x00\x00\x00\x0A\xC5\xCD'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`check_crc` validates the whole signed frame, so it works with data read straight from
|
|
99
|
+
the serial port, without splitting off the trailing bytes:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
check_crc(b'\x01\x03\x00\x00\x00\x0A\xC5\xCD') # True
|
|
103
|
+
check_crc(b'\x01\x03\x00\x00\x00\x0A\xC5\xCE') # False
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## API
|
|
107
|
+
|
|
108
|
+
All three functions accept `bytes`, `bytearray` and `memoryview` (declared as `BytesLike`).
|
|
109
|
+
|
|
110
|
+
| Function | Returns | Description |
|
|
111
|
+
|---------------------------------|---------|-----------------------------------------------------------------|
|
|
112
|
+
| `crc16(data)` | `bytes` | 2-byte checksum of `data`, low byte first |
|
|
113
|
+
| `add_crc(package)` | `bytes` | `package` with the checksum appended; input type is normalized |
|
|
114
|
+
| `check_crc(package)` | `bool` | `True` if `package` carries a valid trailing checksum |
|
|
115
|
+
|
|
116
|
+
Notes:
|
|
117
|
+
|
|
118
|
+
- `add_crc` always returns `bytes`, even for `bytearray` and `memoryview` input.
|
|
119
|
+
- `check_crc(b'')` is `False`: an empty package has no valid checksum.
|
|
120
|
+
- Functions are pure: they keep no state between calls. The lookup table is built once,
|
|
121
|
+
at import time.
|
|
122
|
+
|
|
123
|
+
## Compatibility
|
|
124
|
+
|
|
125
|
+
- Python **3.9 and newer**, including free-threaded builds. Tested on 3.9 and 3.15 and
|
|
126
|
+
in CI on every version from 3.9 to 3.15.
|
|
127
|
+
- No runtime dependencies.
|
|
128
|
+
- Type hints are shipped with the package (`py.typed`, PEP 561), so mypy and pyright
|
|
129
|
+
see real signatures instead of `Any`.
|
|
130
|
+
|
|
131
|
+
## Development
|
|
132
|
+
|
|
133
|
+
```shell
|
|
134
|
+
$ python -m venv .venv
|
|
135
|
+
$ .venv\Scripts\activate # Windows; on POSIX: source .venv/bin/activate
|
|
136
|
+
$ pip install -e ".[dev]" # installs the package and pytest
|
|
137
|
+
$ python -m pytest
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The test suite covers:
|
|
141
|
+
|
|
142
|
+
- canonical Modbus frames with published checksums,
|
|
143
|
+
- an independent bit-by-bit implementation written from the specification, used as an
|
|
144
|
+
oracle against the fast table-driven code (including random payloads up to 1 KiB),
|
|
145
|
+
- edge cases: empty package, one-byte packages, `bytearray` / `memoryview` input,
|
|
146
|
+
- detection of every single-byte corruption of a signed frame.
|
|
147
|
+
|
|
148
|
+
Project layout:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
modbus_crc/__init__.py the whole library
|
|
152
|
+
tests/test_modbus_crc.py test suite
|
|
153
|
+
pyproject.toml metadata, build config and pytest settings
|
|
154
|
+
.github/workflows/ CI: tests on Python 3.9-3.15, sdist/wheel build check
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
To build and verify a release:
|
|
158
|
+
|
|
159
|
+
```shell
|
|
160
|
+
$ pip install build twine
|
|
161
|
+
$ python -m build
|
|
162
|
+
$ python -m twine check dist/*
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Migrating from 1.x
|
|
166
|
+
|
|
167
|
+
The public API is unchanged, so most projects need no code changes. What did change:
|
|
168
|
+
|
|
169
|
+
- **Python 3.9+ is required.** Python 2 support and the legacy `setup.py` / `setup.cfg`
|
|
170
|
+
are gone; metadata now lives in `pyproject.toml` (PEP 621).
|
|
171
|
+
- **`LOW_BYTES` and `HIGH_BYTES` were removed.** Those were two 256-byte tables with
|
|
172
|
+
inverted naming that leaked from the implementation. The lookup table is now generated
|
|
173
|
+
at import time and is private — use the documented functions instead.
|
|
174
|
+
- **`add_crc` always returns `bytes`.** Previously `add_crc(bytearray(...))` returned a
|
|
175
|
+
`bytearray`; the result type is now stable regardless of the input type.
|
|
176
|
+
- **`modbus_crc.__version__` is available** and is the single source of truth for the
|
|
177
|
+
package version.
|
|
178
|
+
- **The package ships `py.typed`**, so type checkers no longer treat it as untyped.
|
|
179
|
+
- **Faster and clearer internals:** one table instead of two, a single 16-bit register
|
|
180
|
+
instead of two byte variables, no `int()` call per byte (over 20% faster per KB on
|
|
181
|
+
CPython 3.11).
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
Distributed under the BSD 3-Clause license. See [LICENSE.md](LICENSE.md) for details.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
LICENSE.md
|
|
2
|
-
MANIFEST.in
|
|
3
2
|
README.md
|
|
4
|
-
|
|
5
|
-
setup.py
|
|
3
|
+
pyproject.toml
|
|
6
4
|
modbus_crc/__init__.py
|
|
5
|
+
modbus_crc/py.typed
|
|
7
6
|
modbus_crc.egg-info/PKG-INFO
|
|
8
7
|
modbus_crc.egg-info/SOURCES.txt
|
|
9
8
|
modbus_crc.egg-info/dependency_links.txt
|
|
10
9
|
modbus_crc.egg-info/not-zip-safe
|
|
11
|
-
modbus_crc.egg-info/
|
|
10
|
+
modbus_crc.egg-info/requires.txt
|
|
11
|
+
modbus_crc.egg-info/top_level.txt
|
|
12
|
+
tests/test_modbus_crc.py
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "modbus-crc"
|
|
7
|
+
description = "CRC-16 calculation for Modbus protocol"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.9"
|
|
10
|
+
license = "BSD-3-Clause"
|
|
11
|
+
license-files = ["LICENSE.md"]
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Alexey Kuznetsov", email = "webtoucher@ya.ru" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["modbus", "crc", "crc16", "checksum", "serial", "rtu"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Topic :: Software Development :: Libraries",
|
|
20
|
+
"Operating System :: Microsoft :: Windows",
|
|
21
|
+
"Operating System :: POSIX",
|
|
22
|
+
"Operating System :: Unix",
|
|
23
|
+
"Operating System :: MacOS",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
dynamic = ["version"]
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = ["pytest>=7"]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/webtoucher/modbus-crc"
|
|
34
|
+
Repository = "https://github.com/webtoucher/modbus-crc"
|
|
35
|
+
Issues = "https://github.com/webtoucher/modbus-crc/issues"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools]
|
|
38
|
+
packages = ["modbus_crc"]
|
|
39
|
+
zip-safe = false
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.dynamic]
|
|
42
|
+
version = { attr = "modbus_crc.__version__" }
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.package-data]
|
|
45
|
+
modbus_crc = ["py.typed"]
|
|
46
|
+
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
testpaths = ["tests"]
|
|
49
|
+
addopts = "-ra"
|
modbus_crc-2.0/setup.cfg
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Tests for the modbus_crc package."""
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from modbus_crc import __all__, add_crc, check_crc, crc16
|
|
8
|
+
|
|
9
|
+
# Frame without checksum and the checksum exactly as it goes on the wire
|
|
10
|
+
# (low byte first). The first three frames are canonical Modbus examples.
|
|
11
|
+
KNOWN_FRAMES = [
|
|
12
|
+
(b"\x01\x03\x00\x00\x00\x0A", b"\xC5\xCD"),
|
|
13
|
+
(b"\x11\x03\x00\x6B\x00\x03", b"\x76\x87"),
|
|
14
|
+
(b"\x00\x12\x34\xAB\xCD\xEF", b"\xD2\xD4"),
|
|
15
|
+
(b"\xFF" * 8, b"\x01\x8F"),
|
|
16
|
+
(b"\x00", b"\xBF\x40"),
|
|
17
|
+
(b"\xFF", b"\xFF\x00"),
|
|
18
|
+
(b"", b"\xFF\xFF"),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _reference_crc16(data: bytes) -> bytes:
|
|
23
|
+
"""Bit-by-bit CRC-16/MODBUS, written straight from the protocol spec."""
|
|
24
|
+
crc = 0xFFFF
|
|
25
|
+
for byte in data:
|
|
26
|
+
crc ^= byte
|
|
27
|
+
for _ in range(8):
|
|
28
|
+
crc = (crc >> 1) ^ 0xA001 if crc & 1 else crc >> 1
|
|
29
|
+
return bytes((crc & 0xFF, crc >> 8))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.parametrize("frame, checksum", KNOWN_FRAMES)
|
|
33
|
+
def test_known_frames(frame, checksum):
|
|
34
|
+
assert crc16(frame) == checksum
|
|
35
|
+
assert add_crc(frame) == frame + checksum
|
|
36
|
+
assert check_crc(frame + checksum)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.parametrize("frame, checksum", KNOWN_FRAMES)
|
|
40
|
+
def test_reference_oracle_agrees_with_known_frames(frame, checksum):
|
|
41
|
+
assert _reference_crc16(frame) == checksum
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_table_matches_reference_implementation():
|
|
45
|
+
rng = random.Random(20231115)
|
|
46
|
+
for length in [0, 1, 2, 3, 8, 32, 255, 1024]:
|
|
47
|
+
data = bytes(rng.randrange(256) for _ in range(length))
|
|
48
|
+
assert crc16(data) == _reference_crc16(data)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_empty_package_is_rejected():
|
|
52
|
+
assert not check_crc(b"")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_corrupted_package_is_rejected():
|
|
56
|
+
signed = add_crc(b"\x01\x03\x00\x00\x00\x0A")
|
|
57
|
+
for index in range(len(signed)):
|
|
58
|
+
corrupted = bytearray(signed)
|
|
59
|
+
corrupted[index] ^= 0x01
|
|
60
|
+
assert not check_crc(corrupted), f"corruption at byte {index} not detected"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_unsigned_package_is_rejected():
|
|
64
|
+
assert not check_crc(b"\x01\x03\x00\x00\x00\x0A")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_accepts_bytes_like_objects():
|
|
68
|
+
payload = b"\x01\x03\x00\x00\x00\x0A"
|
|
69
|
+
assert crc16(bytearray(payload)) == crc16(payload)
|
|
70
|
+
assert crc16(memoryview(payload)) == crc16(payload)
|
|
71
|
+
assert isinstance(add_crc(bytearray(payload)), bytes)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_checksum_is_two_bytes():
|
|
75
|
+
assert crc16(b"\x01\x03") == crc16(b"\x01\x03")
|
|
76
|
+
assert len(crc16(b"\x01\x03")) == 2
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_public_api_is_explicit():
|
|
80
|
+
assert __all__ == ["crc16", "add_crc", "check_crc"]
|
modbus-crc-1.2/MANIFEST.in
DELETED
modbus-crc-1.2/PKG-INFO
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: modbus-crc
|
|
3
|
-
Version: 1.2
|
|
4
|
-
Summary: CRC-16 calculation for Modbus protocol
|
|
5
|
-
Home-page: https://github.com/webtoucher/modbus-crc
|
|
6
|
-
Author: Alexey Kuznetsov
|
|
7
|
-
Author-email: mirakuru@webtoucher.ru
|
|
8
|
-
License: BSD-3-Clause
|
|
9
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
12
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
-
Classifier: Operating System :: POSIX
|
|
16
|
-
Classifier: Operating System :: Unix
|
|
17
|
-
Classifier: Operating System :: MacOS
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
License-File: LICENSE.md
|
|
20
|
-
|
|
21
|
-
# Modbus CRC-16
|
|
22
|
-
|
|
23
|
-

|
|
24
|
-
[](https://pypi.python.org/pypi/modbus-crc)
|
|
25
|
-
[](https://pypi.python.org/pypi/modbus-crc)
|
|
26
|
-
[](https://pypi.python.org/pypi/modbus-crc)
|
|
27
|
-
|
|
28
|
-
CRC-16 calculation library for Modbus protocol
|
|
29
|
-
|
|
30
|
-
## Installation
|
|
31
|
-
|
|
32
|
-
Install it with pip:
|
|
33
|
-
|
|
34
|
-
```shell
|
|
35
|
-
$ pip install modbus-crc
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Or you can add it as dependency in requirements.txt file of your python application:
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
modbus-crc~=1.2
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Usage
|
|
45
|
-
|
|
46
|
-
For signing byte package use `add_crc`:
|
|
47
|
-
|
|
48
|
-
```python
|
|
49
|
-
from modbus_crc import add_crc
|
|
50
|
-
|
|
51
|
-
signed_package = add_crc(b'\x00\x12\x34\xAB\xCD\xEF')
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Result is `b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4`. For validation signed package use `check_crc`:
|
|
55
|
-
|
|
56
|
-
```python
|
|
57
|
-
from modbus_crc import check_crc
|
|
58
|
-
|
|
59
|
-
if not check_crc(b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'):
|
|
60
|
-
raise SomeException()
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Testing
|
|
64
|
-
|
|
65
|
-
Use [tox](https://tox.readthedocs.io) for testing
|
|
66
|
-
|
|
67
|
-
```shell
|
|
68
|
-
$ pip install tox
|
|
69
|
-
$ python -m tox
|
|
70
|
-
```
|
modbus-crc-1.2/README.md
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# Modbus CRC-16
|
|
2
|
-
|
|
3
|
-

|
|
4
|
-
[](https://pypi.python.org/pypi/modbus-crc)
|
|
5
|
-
[](https://pypi.python.org/pypi/modbus-crc)
|
|
6
|
-
[](https://pypi.python.org/pypi/modbus-crc)
|
|
7
|
-
|
|
8
|
-
CRC-16 calculation library for Modbus protocol
|
|
9
|
-
|
|
10
|
-
## Installation
|
|
11
|
-
|
|
12
|
-
Install it with pip:
|
|
13
|
-
|
|
14
|
-
```shell
|
|
15
|
-
$ pip install modbus-crc
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Or you can add it as dependency in requirements.txt file of your python application:
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
modbus-crc~=1.2
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Usage
|
|
25
|
-
|
|
26
|
-
For signing byte package use `add_crc`:
|
|
27
|
-
|
|
28
|
-
```python
|
|
29
|
-
from modbus_crc import add_crc
|
|
30
|
-
|
|
31
|
-
signed_package = add_crc(b'\x00\x12\x34\xAB\xCD\xEF')
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Result is `b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4`. For validation signed package use `check_crc`:
|
|
35
|
-
|
|
36
|
-
```python
|
|
37
|
-
from modbus_crc import check_crc
|
|
38
|
-
|
|
39
|
-
if not check_crc(b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'):
|
|
40
|
-
raise SomeException()
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Testing
|
|
44
|
-
|
|
45
|
-
Use [tox](https://tox.readthedocs.io) for testing
|
|
46
|
-
|
|
47
|
-
```shell
|
|
48
|
-
$ pip install tox
|
|
49
|
-
$ python -m tox
|
|
50
|
-
```
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# coding=utf8
|
|
2
|
-
"""
|
|
3
|
-
CRC-16 calculation for Modbus protocol
|
|
4
|
-
Copyright (c) 2023 webtoucher
|
|
5
|
-
Distributed under the BSD 3-Clause license. See LICENSE for more info.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
LOW_BYTES = b'\
|
|
9
|
-
\x00\xC0\xC1\x01\xC3\x03\x02\xC2\xC6\x06\x07\xC7\x05\xC5\xC4\x04\
|
|
10
|
-
\xCC\x0C\x0D\xCD\x0F\xCF\xCE\x0E\x0A\xCA\xCB\x0B\xC9\x09\x08\xC8\
|
|
11
|
-
\xD8\x18\x19\xD9\x1B\xDB\xDA\x1A\x1E\xDE\xDF\x1F\xDD\x1D\x1C\xDC\
|
|
12
|
-
\x14\xD4\xD5\x15\xD7\x17\x16\xD6\xD2\x12\x13\xD3\x11\xD1\xD0\x10\
|
|
13
|
-
\xF0\x30\x31\xF1\x33\xF3\xF2\x32\x36\xF6\xF7\x37\xF5\x35\x34\xF4\
|
|
14
|
-
\x3C\xFC\xFD\x3D\xFF\x3F\x3E\xFE\xFA\x3A\x3B\xFB\x39\xF9\xF8\x38\
|
|
15
|
-
\x28\xE8\xE9\x29\xEB\x2B\x2A\xEA\xEE\x2E\x2F\xEF\x2D\xED\xEC\x2C\
|
|
16
|
-
\xE4\x24\x25\xE5\x27\xE7\xE6\x26\x22\xE2\xE3\x23\xE1\x21\x20\xE0\
|
|
17
|
-
\xA0\x60\x61\xA1\x63\xA3\xA2\x62\x66\xA6\xA7\x67\xA5\x65\x64\xA4\
|
|
18
|
-
\x6C\xAC\xAD\x6D\xAF\x6F\x6E\xAE\xAA\x6A\x6B\xAB\x69\xA9\xA8\x68\
|
|
19
|
-
\x78\xB8\xB9\x79\xBB\x7B\x7A\xBA\xBE\x7E\x7F\xBF\x7D\xBD\xBC\x7C\
|
|
20
|
-
\xB4\x74\x75\xB5\x77\xB7\xB6\x76\x72\xB2\xB3\x73\xB1\x71\x70\xB0\
|
|
21
|
-
\x50\x90\x91\x51\x93\x53\x52\x92\x96\x56\x57\x97\x55\x95\x94\x54\
|
|
22
|
-
\x9C\x5C\x5D\x9D\x5F\x9F\x9E\x5E\x5A\x9A\x9B\x5B\x99\x59\x58\x98\
|
|
23
|
-
\x88\x48\x49\x89\x4B\x8B\x8A\x4A\x4E\x8E\x8F\x4F\x8D\x4D\x4C\x8C\
|
|
24
|
-
\x44\x84\x85\x45\x87\x47\x46\x86\x82\x42\x43\x83\x41\x81\x80\x40'
|
|
25
|
-
|
|
26
|
-
HIGH_BYTES = b'\
|
|
27
|
-
\x00\xC1\x81\x40\x01\xC0\x80\x41\x01\xC0\x80\x41\x00\xC1\x81\x40\
|
|
28
|
-
\x01\xC0\x80\x41\x00\xC1\x81\x40\x00\xC1\x81\x40\x01\xC0\x80\x41\
|
|
29
|
-
\x01\xC0\x80\x41\x00\xC1\x81\x40\x00\xC1\x81\x40\x01\xC0\x80\x41\
|
|
30
|
-
\x00\xC1\x81\x40\x01\xC0\x80\x41\x01\xC0\x80\x41\x00\xC1\x81\x40\
|
|
31
|
-
\x01\xC0\x80\x41\x00\xC1\x81\x40\x00\xC1\x81\x40\x01\xC0\x80\x41\
|
|
32
|
-
\x00\xC1\x81\x40\x01\xC0\x80\x41\x01\xC0\x80\x41\x00\xC1\x81\x40\
|
|
33
|
-
\x00\xC1\x81\x40\x01\xC0\x80\x41\x01\xC0\x80\x41\x00\xC1\x81\x40\
|
|
34
|
-
\x01\xC0\x80\x41\x00\xC1\x81\x40\x00\xC1\x81\x40\x01\xC0\x80\x41\
|
|
35
|
-
\x01\xC0\x80\x41\x00\xC1\x81\x40\x00\xC1\x81\x40\x01\xC0\x80\x41\
|
|
36
|
-
\x00\xC1\x81\x40\x01\xC0\x80\x41\x01\xC0\x80\x41\x00\xC1\x81\x40\
|
|
37
|
-
\x00\xC1\x81\x40\x01\xC0\x80\x41\x01\xC0\x80\x41\x00\xC1\x81\x40\
|
|
38
|
-
\x01\xC0\x80\x41\x00\xC1\x81\x40\x00\xC1\x81\x40\x01\xC0\x80\x41\
|
|
39
|
-
\x00\xC1\x81\x40\x01\xC0\x80\x41\x01\xC0\x80\x41\x00\xC1\x81\x40\
|
|
40
|
-
\x01\xC0\x80\x41\x00\xC1\x81\x40\x00\xC1\x81\x40\x01\xC0\x80\x41\
|
|
41
|
-
\x01\xC0\x80\x41\x00\xC1\x81\x40\x00\xC1\x81\x40\x01\xC0\x80\x41\
|
|
42
|
-
\x00\xC1\x81\x40\x01\xC0\x80\x41\x01\xC0\x80\x41\x00\xC1\x81\x40'
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def crc16(data: bytes) -> bytes:
|
|
46
|
-
"""Calculate CRC-16 for Modbus."""
|
|
47
|
-
crc_high = 0xFF
|
|
48
|
-
crc_low = 0xFF
|
|
49
|
-
|
|
50
|
-
for byte in data:
|
|
51
|
-
index = crc_high ^ int(byte)
|
|
52
|
-
crc_high = crc_low ^ HIGH_BYTES[index]
|
|
53
|
-
crc_low = LOW_BYTES[index]
|
|
54
|
-
|
|
55
|
-
return bytes([crc_high, crc_low])
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def add_crc(package: bytes) -> bytes:
|
|
59
|
-
"""Add CRC-16 to bytes package."""
|
|
60
|
-
return package + crc16(package)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def check_crc(package: bytes) -> bool:
|
|
64
|
-
"""Validate signed bytes package."""
|
|
65
|
-
return crc16(package) == b'\x00\x00'
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if __name__ == "__main__":
|
|
69
|
-
pass
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: modbus-crc
|
|
3
|
-
Version: 1.2
|
|
4
|
-
Summary: CRC-16 calculation for Modbus protocol
|
|
5
|
-
Home-page: https://github.com/webtoucher/modbus-crc
|
|
6
|
-
Author: Alexey Kuznetsov
|
|
7
|
-
Author-email: mirakuru@webtoucher.ru
|
|
8
|
-
License: BSD-3-Clause
|
|
9
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
12
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
-
Classifier: Operating System :: POSIX
|
|
16
|
-
Classifier: Operating System :: Unix
|
|
17
|
-
Classifier: Operating System :: MacOS
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
License-File: LICENSE.md
|
|
20
|
-
|
|
21
|
-
# Modbus CRC-16
|
|
22
|
-
|
|
23
|
-

|
|
24
|
-
[](https://pypi.python.org/pypi/modbus-crc)
|
|
25
|
-
[](https://pypi.python.org/pypi/modbus-crc)
|
|
26
|
-
[](https://pypi.python.org/pypi/modbus-crc)
|
|
27
|
-
|
|
28
|
-
CRC-16 calculation library for Modbus protocol
|
|
29
|
-
|
|
30
|
-
## Installation
|
|
31
|
-
|
|
32
|
-
Install it with pip:
|
|
33
|
-
|
|
34
|
-
```shell
|
|
35
|
-
$ pip install modbus-crc
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Or you can add it as dependency in requirements.txt file of your python application:
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
modbus-crc~=1.2
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Usage
|
|
45
|
-
|
|
46
|
-
For signing byte package use `add_crc`:
|
|
47
|
-
|
|
48
|
-
```python
|
|
49
|
-
from modbus_crc import add_crc
|
|
50
|
-
|
|
51
|
-
signed_package = add_crc(b'\x00\x12\x34\xAB\xCD\xEF')
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Result is `b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4`. For validation signed package use `check_crc`:
|
|
55
|
-
|
|
56
|
-
```python
|
|
57
|
-
from modbus_crc import check_crc
|
|
58
|
-
|
|
59
|
-
if not check_crc(b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'):
|
|
60
|
-
raise SomeException()
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Testing
|
|
64
|
-
|
|
65
|
-
Use [tox](https://tox.readthedocs.io) for testing
|
|
66
|
-
|
|
67
|
-
```shell
|
|
68
|
-
$ pip install tox
|
|
69
|
-
$ python -m tox
|
|
70
|
-
```
|
modbus-crc-1.2/setup.cfg
DELETED
modbus-crc-1.2/setup.py
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from setuptools import setup, find_packages
|
|
2
|
-
|
|
3
|
-
setup(name='modbus-crc',
|
|
4
|
-
version='1.2',
|
|
5
|
-
url='https://github.com/webtoucher/modbus-crc',
|
|
6
|
-
license='BSD-3-Clause',
|
|
7
|
-
author='Alexey Kuznetsov',
|
|
8
|
-
author_email='mirakuru@webtoucher.ru',
|
|
9
|
-
description='CRC-16 calculation for Modbus protocol',
|
|
10
|
-
classifiers=[
|
|
11
|
-
'Development Status :: 5 - Production/Stable',
|
|
12
|
-
'Intended Audience :: Developers',
|
|
13
|
-
'Topic :: Software Development :: Libraries',
|
|
14
|
-
'License :: OSI Approved :: BSD License',
|
|
15
|
-
'Programming Language :: Python :: 3',
|
|
16
|
-
'Operating System :: Microsoft :: Windows',
|
|
17
|
-
'Operating System :: POSIX',
|
|
18
|
-
'Operating System :: Unix',
|
|
19
|
-
'Operating System :: MacOS',
|
|
20
|
-
],
|
|
21
|
-
packages=find_packages(exclude='tests'),
|
|
22
|
-
long_description=open('README.md').read(),
|
|
23
|
-
long_description_content_type='text/markdown',
|
|
24
|
-
zip_safe=False)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|