scietex.hal.vfd 2.0.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.
- scietex_hal_vfd-2.0.0/LICENSE +21 -0
- scietex_hal_vfd-2.0.0/PKG-INFO +222 -0
- scietex_hal_vfd-2.0.0/README.md +174 -0
- scietex_hal_vfd-2.0.0/pyproject.toml +57 -0
- scietex_hal_vfd-2.0.0/setup.cfg +4 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/__init__.py +3 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/__init__.py +47 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/capabilities.py +49 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/data.py +122 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/protocol.py +129 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/rs485.py +153 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/__init__.py +5 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/__init__.py +26 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/client.py +384 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/errors.py +67 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/registers.py +238 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/rs485.py +166 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/modbus_drivers.py +60 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/py.typed +0 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/version.py +8 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/__init__.py +11 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/__init__.py +35 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/client.py +402 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/errors.py +99 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/registers.py +316 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/rs485.py +289 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/__init__.py +28 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/client.py +338 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/errors.py +77 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/registers.py +235 -0
- scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/rs485.py +216 -0
- scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/PKG-INFO +222 -0
- scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/SOURCES.txt +41 -0
- scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/dependency_links.txt +1 -0
- scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/entry_points.txt +9 -0
- scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/requires.txt +15 -0
- scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/top_level.txt +1 -0
- scietex_hal_vfd-2.0.0/tests/test_modbus_client.py +283 -0
- scietex_hal_vfd-2.0.0/tests/test_modbus_drivers.py +69 -0
- scietex_hal_vfd-2.0.0/tests/test_registers.py +132 -0
- scietex_hal_vfd-2.0.0/tests/test_version.py +47 -0
- scietex_hal_vfd-2.0.0/tests/test_vesper_e4_8400_registers.py +149 -0
- scietex_hal_vfd-2.0.0/tests/test_vesper_e5_8200_registers.py +107 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Anton Bondarenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scietex.hal.vfd
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Scietex VFD HAL package
|
|
5
|
+
Author-email: Anton Bondarenko <bond.anton@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Anton Bondarenko
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
Project-URL: Homepage, https://github.com/bond-anton/scietex.hal.vfd
|
|
28
|
+
Project-URL: Bug Tracker, https://github.com/bond-anton/scietex.hal.vfd/issues
|
|
29
|
+
Classifier: Programming Language :: Python :: 3
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Operating System :: OS Independent
|
|
32
|
+
Requires-Python: >=3.10
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
License-File: LICENSE
|
|
35
|
+
Requires-Dist: msgspec[yaml]~=0.20.0
|
|
36
|
+
Requires-Dist: scietex.hal.serial<3.0.0,>=2.4.0
|
|
37
|
+
Requires-Dist: pymodbus~=3.15
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: tox>=4.46.0; extra == "dev"
|
|
40
|
+
Provides-Extra: test
|
|
41
|
+
Requires-Dist: pytest; extra == "test"
|
|
42
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
|
43
|
+
Requires-Dist: pytest-xdist; extra == "test"
|
|
44
|
+
Provides-Extra: lint
|
|
45
|
+
Requires-Dist: ruff; extra == "lint"
|
|
46
|
+
Requires-Dist: ty; extra == "lint"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
# scietex.hal.vfd
|
|
50
|
+
|
|
51
|
+
The `scietex.hal.vfd` package is a Python library for interfacing with variable
|
|
52
|
+
frequency drives (VFD). It defines a transport-agnostic driver contract
|
|
53
|
+
(`VFDProtocol`) plus the data structures and capability model shared by every
|
|
54
|
+
driver, and ships concrete implementations for three drive families.
|
|
55
|
+
|
|
56
|
+
A driver reaches a drive over one of two transports:
|
|
57
|
+
|
|
58
|
+
- **Direct RS485** — the driver owns the serial port and speaks Modbus RTU to
|
|
59
|
+
the drive itself (`scietex.hal.vfd.base.rs485.VFD`).
|
|
60
|
+
- **Scietex Modbus gateway** — the gateway owns the RS485 bus, and the driver
|
|
61
|
+
speaks Modbus/TCP to a gateway device entry that routes to the drive by unit
|
|
62
|
+
id (`scietex.hal.vfd.intek.spe_b.client.IntekSPEBModbusVFD`).
|
|
63
|
+
|
|
64
|
+
## Features
|
|
65
|
+
|
|
66
|
+
- `VFDProtocol`, a runtime-checkable contract: connect, disconnect, read
|
|
67
|
+
parameters, read state, read and reset faults, start, stop and set the
|
|
68
|
+
frequency setpoint.
|
|
69
|
+
- `VFDCapabilities`, which advertises optional control surface (direction, jog,
|
|
70
|
+
freewheel stop, fault reset, frequency setpoint) so a caller can reject an
|
|
71
|
+
unsupported command before dispatching it.
|
|
72
|
+
- `VFDParameters`, which reports frequency in both Hz and percent of the
|
|
73
|
+
configured maximum, plus output current, voltage and power, and the optional
|
|
74
|
+
DC bus voltage and temperatures a drive exposes. An optional field is `None`
|
|
75
|
+
when the drive does not report it, which is distinct from a reported `0.0`.
|
|
76
|
+
- Implemented drive models, each with both transports:
|
|
77
|
+
- `Intek SPE-B` — direct RS485 (`SPEvB`) and gateway (`IntekSPEBModbusVFD`).
|
|
78
|
+
- `Vesper E5-8200` — direct RS485 (`VesperE58200`) and gateway
|
|
79
|
+
(`VesperE58200ModbusVFD`).
|
|
80
|
+
- `Vesper E4-8400` / `E4-P8402` — direct RS485 (`VesperE48400`) and gateway
|
|
81
|
+
(`VesperE48400ModbusVFD`).
|
|
82
|
+
- Driver descriptors for the gateway (`scietex.modbus.drivers` entry-point
|
|
83
|
+
group) and driver classes for a service (`scietex.hal.vfd.drivers` group).
|
|
84
|
+
|
|
85
|
+
### What each drive reports
|
|
86
|
+
|
|
87
|
+
| Field | Intek SPE-B | Vesper E5-8200 | Vesper E4-8400 |
|
|
88
|
+
| --- | --- | --- | --- |
|
|
89
|
+
| `frequency_hz` | yes | yes | yes |
|
|
90
|
+
| `frequency_percent` | yes | yes | yes |
|
|
91
|
+
| `output_current` | yes | yes | yes |
|
|
92
|
+
| `output_voltage` | yes | yes | yes |
|
|
93
|
+
| `output_power` | yes | no (`0.0`) | no (`0.0`) |
|
|
94
|
+
| `dc_bus_voltage` | yes | yes | yes |
|
|
95
|
+
| `heatsink_temperature_c` | no (`None`) | no (`None`) | yes |
|
|
96
|
+
| `motor_temperature_c` | no (`None`) | no (`None`) | no (`None`) |
|
|
97
|
+
|
|
98
|
+
Only the E4-8400 exposes a live temperature reading (`004Dh`, U1-46). The other
|
|
99
|
+
two report overheat only as a fault code, and a fault is not a temperature.
|
|
100
|
+
|
|
101
|
+
## Installation
|
|
102
|
+
|
|
103
|
+
Install the package via pip (assuming it's published to PyPI):
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pip install scietex.hal.vfd
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Alternatively, clone the repository and install locally:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
git clone https://github.com/bond-anton/scietex.hal.vfd.git
|
|
113
|
+
cd scietex.hal.vfd
|
|
114
|
+
pip install .
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Requirements
|
|
118
|
+
|
|
119
|
+
- Python 3.10 or higher.
|
|
120
|
+
- `scietex.hal.serial` 2.4 or newer (RS485 transport and Modbus gateway
|
|
121
|
+
descriptors).
|
|
122
|
+
- `pymodbus` 3.15 or newer (Modbus/TCP client for the gateway transport).
|
|
123
|
+
|
|
124
|
+
## Usage
|
|
125
|
+
|
|
126
|
+
Every driver implements `VFDProtocol`: `connect`, `close`, `read_data`,
|
|
127
|
+
`read_parameters`, `read_state`, `read_error_code`, `clear_error`, `start`,
|
|
128
|
+
`stop` and `set_frequency`. Check `driver.capabilities` before issuing an
|
|
129
|
+
optional command.
|
|
130
|
+
|
|
131
|
+
Direct RS485 with the Intek SPE-B VFD:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import asyncio
|
|
135
|
+
|
|
136
|
+
from scietex.hal.serial import ModbusSerialConnectionConfig
|
|
137
|
+
from scietex.hal.vfd.intek import SPEvB
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
async def main(serial_conf: ModbusSerialConnectionConfig):
|
|
141
|
+
"""Main function."""
|
|
142
|
+
vfd = SPEvB(serial_conf, address=1, label="Intek VFD")
|
|
143
|
+
data = await vfd.read_data()
|
|
144
|
+
print(data)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == "__main__":
|
|
148
|
+
serial_configuration = ModbusSerialConnectionConfig(
|
|
149
|
+
port="/dev/serial0", baudrate=19200
|
|
150
|
+
)
|
|
151
|
+
asyncio.run(main(serial_configuration))
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The same drive behind the Scietex Modbus gateway:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import asyncio
|
|
158
|
+
|
|
159
|
+
from scietex.hal.vfd.intek import IntekSPEBModbusVFD
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
async def main():
|
|
163
|
+
"""Main function."""
|
|
164
|
+
vfd = IntekSPEBModbusVFD("gateway.local", port=502, unit_id=1)
|
|
165
|
+
await vfd.connect()
|
|
166
|
+
try:
|
|
167
|
+
print(await vfd.read_data())
|
|
168
|
+
print(await vfd.read_error_code())
|
|
169
|
+
finally:
|
|
170
|
+
await vfd.close()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__":
|
|
174
|
+
asyncio.run(main())
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
A Vesper drive behind the gateway works the same way:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from scietex.hal.vfd.vesper import VesperE48400ModbusVFD
|
|
181
|
+
|
|
182
|
+
vfd = VesperE48400ModbusVFD("gateway.local", port=502, unit_id=1)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`set_frequency` takes Hz, not percent: the caller works in engineering units and
|
|
186
|
+
each driver converts with its own maximum-frequency parameter. The E5-8200's
|
|
187
|
+
communication manual names no maximum-frequency parameter, so its driver takes
|
|
188
|
+
`max_frequency_hz` from the constructor (default `50.0`, the factory base
|
|
189
|
+
frequency); a caller whose drive runs a different maximum must pass it, or
|
|
190
|
+
`frequency_percent` will be wrong.
|
|
191
|
+
|
|
192
|
+
A drive that has never been configured for the gateway answers neither
|
|
193
|
+
transport. See the per-family references for the register maps, the fault tables
|
|
194
|
+
and the settings the gateway requires:
|
|
195
|
+
|
|
196
|
+
- [`intek/docs/protocol.md`](src/scietex/hal/vfd/intek/docs/protocol.md) —
|
|
197
|
+
Intek SPE-B (`Pd.01`/`Pd.05`/`Pd.02`).
|
|
198
|
+
- [`vesper/docs/README.md`](src/scietex/hal/vfd/vesper/docs/README.md) — Vesper
|
|
199
|
+
E5-8200 and E4-8400, plus the transport facts both manuals share.
|
|
200
|
+
|
|
201
|
+
## Contributing
|
|
202
|
+
|
|
203
|
+
Contributions are welcome! To contribute:
|
|
204
|
+
|
|
205
|
+
1. Fork the repository.
|
|
206
|
+
2. Create a feature branch (`git checkout -b feature/YourFeature`).
|
|
207
|
+
3. Commit your changes (`git commit -m "Add your message"`).
|
|
208
|
+
4. Push to the branch (`git push origin feature/YourFeature`).
|
|
209
|
+
5. Open a Pull Request.
|
|
210
|
+
|
|
211
|
+
Run the checks before opening a pull request:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
tox -e format,lint,type,test
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`format` and `lint` run `ruff`, `type` runs `ty`, and `test` runs `pytest` across
|
|
218
|
+
Python 3.10, 3.12 and 3.14.
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
This project is licensed under the **MIT License** - see the LICENSE file for details.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# scietex.hal.vfd
|
|
2
|
+
|
|
3
|
+
The `scietex.hal.vfd` package is a Python library for interfacing with variable
|
|
4
|
+
frequency drives (VFD). It defines a transport-agnostic driver contract
|
|
5
|
+
(`VFDProtocol`) plus the data structures and capability model shared by every
|
|
6
|
+
driver, and ships concrete implementations for three drive families.
|
|
7
|
+
|
|
8
|
+
A driver reaches a drive over one of two transports:
|
|
9
|
+
|
|
10
|
+
- **Direct RS485** — the driver owns the serial port and speaks Modbus RTU to
|
|
11
|
+
the drive itself (`scietex.hal.vfd.base.rs485.VFD`).
|
|
12
|
+
- **Scietex Modbus gateway** — the gateway owns the RS485 bus, and the driver
|
|
13
|
+
speaks Modbus/TCP to a gateway device entry that routes to the drive by unit
|
|
14
|
+
id (`scietex.hal.vfd.intek.spe_b.client.IntekSPEBModbusVFD`).
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- `VFDProtocol`, a runtime-checkable contract: connect, disconnect, read
|
|
19
|
+
parameters, read state, read and reset faults, start, stop and set the
|
|
20
|
+
frequency setpoint.
|
|
21
|
+
- `VFDCapabilities`, which advertises optional control surface (direction, jog,
|
|
22
|
+
freewheel stop, fault reset, frequency setpoint) so a caller can reject an
|
|
23
|
+
unsupported command before dispatching it.
|
|
24
|
+
- `VFDParameters`, which reports frequency in both Hz and percent of the
|
|
25
|
+
configured maximum, plus output current, voltage and power, and the optional
|
|
26
|
+
DC bus voltage and temperatures a drive exposes. An optional field is `None`
|
|
27
|
+
when the drive does not report it, which is distinct from a reported `0.0`.
|
|
28
|
+
- Implemented drive models, each with both transports:
|
|
29
|
+
- `Intek SPE-B` — direct RS485 (`SPEvB`) and gateway (`IntekSPEBModbusVFD`).
|
|
30
|
+
- `Vesper E5-8200` — direct RS485 (`VesperE58200`) and gateway
|
|
31
|
+
(`VesperE58200ModbusVFD`).
|
|
32
|
+
- `Vesper E4-8400` / `E4-P8402` — direct RS485 (`VesperE48400`) and gateway
|
|
33
|
+
(`VesperE48400ModbusVFD`).
|
|
34
|
+
- Driver descriptors for the gateway (`scietex.modbus.drivers` entry-point
|
|
35
|
+
group) and driver classes for a service (`scietex.hal.vfd.drivers` group).
|
|
36
|
+
|
|
37
|
+
### What each drive reports
|
|
38
|
+
|
|
39
|
+
| Field | Intek SPE-B | Vesper E5-8200 | Vesper E4-8400 |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| `frequency_hz` | yes | yes | yes |
|
|
42
|
+
| `frequency_percent` | yes | yes | yes |
|
|
43
|
+
| `output_current` | yes | yes | yes |
|
|
44
|
+
| `output_voltage` | yes | yes | yes |
|
|
45
|
+
| `output_power` | yes | no (`0.0`) | no (`0.0`) |
|
|
46
|
+
| `dc_bus_voltage` | yes | yes | yes |
|
|
47
|
+
| `heatsink_temperature_c` | no (`None`) | no (`None`) | yes |
|
|
48
|
+
| `motor_temperature_c` | no (`None`) | no (`None`) | no (`None`) |
|
|
49
|
+
|
|
50
|
+
Only the E4-8400 exposes a live temperature reading (`004Dh`, U1-46). The other
|
|
51
|
+
two report overheat only as a fault code, and a fault is not a temperature.
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
Install the package via pip (assuming it's published to PyPI):
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install scietex.hal.vfd
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Alternatively, clone the repository and install locally:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git clone https://github.com/bond-anton/scietex.hal.vfd.git
|
|
65
|
+
cd scietex.hal.vfd
|
|
66
|
+
pip install .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Requirements
|
|
70
|
+
|
|
71
|
+
- Python 3.10 or higher.
|
|
72
|
+
- `scietex.hal.serial` 2.4 or newer (RS485 transport and Modbus gateway
|
|
73
|
+
descriptors).
|
|
74
|
+
- `pymodbus` 3.15 or newer (Modbus/TCP client for the gateway transport).
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
Every driver implements `VFDProtocol`: `connect`, `close`, `read_data`,
|
|
79
|
+
`read_parameters`, `read_state`, `read_error_code`, `clear_error`, `start`,
|
|
80
|
+
`stop` and `set_frequency`. Check `driver.capabilities` before issuing an
|
|
81
|
+
optional command.
|
|
82
|
+
|
|
83
|
+
Direct RS485 with the Intek SPE-B VFD:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
import asyncio
|
|
87
|
+
|
|
88
|
+
from scietex.hal.serial import ModbusSerialConnectionConfig
|
|
89
|
+
from scietex.hal.vfd.intek import SPEvB
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def main(serial_conf: ModbusSerialConnectionConfig):
|
|
93
|
+
"""Main function."""
|
|
94
|
+
vfd = SPEvB(serial_conf, address=1, label="Intek VFD")
|
|
95
|
+
data = await vfd.read_data()
|
|
96
|
+
print(data)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
serial_configuration = ModbusSerialConnectionConfig(
|
|
101
|
+
port="/dev/serial0", baudrate=19200
|
|
102
|
+
)
|
|
103
|
+
asyncio.run(main(serial_configuration))
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The same drive behind the Scietex Modbus gateway:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import asyncio
|
|
110
|
+
|
|
111
|
+
from scietex.hal.vfd.intek import IntekSPEBModbusVFD
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def main():
|
|
115
|
+
"""Main function."""
|
|
116
|
+
vfd = IntekSPEBModbusVFD("gateway.local", port=502, unit_id=1)
|
|
117
|
+
await vfd.connect()
|
|
118
|
+
try:
|
|
119
|
+
print(await vfd.read_data())
|
|
120
|
+
print(await vfd.read_error_code())
|
|
121
|
+
finally:
|
|
122
|
+
await vfd.close()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if __name__ == "__main__":
|
|
126
|
+
asyncio.run(main())
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
A Vesper drive behind the gateway works the same way:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from scietex.hal.vfd.vesper import VesperE48400ModbusVFD
|
|
133
|
+
|
|
134
|
+
vfd = VesperE48400ModbusVFD("gateway.local", port=502, unit_id=1)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`set_frequency` takes Hz, not percent: the caller works in engineering units and
|
|
138
|
+
each driver converts with its own maximum-frequency parameter. The E5-8200's
|
|
139
|
+
communication manual names no maximum-frequency parameter, so its driver takes
|
|
140
|
+
`max_frequency_hz` from the constructor (default `50.0`, the factory base
|
|
141
|
+
frequency); a caller whose drive runs a different maximum must pass it, or
|
|
142
|
+
`frequency_percent` will be wrong.
|
|
143
|
+
|
|
144
|
+
A drive that has never been configured for the gateway answers neither
|
|
145
|
+
transport. See the per-family references for the register maps, the fault tables
|
|
146
|
+
and the settings the gateway requires:
|
|
147
|
+
|
|
148
|
+
- [`intek/docs/protocol.md`](src/scietex/hal/vfd/intek/docs/protocol.md) —
|
|
149
|
+
Intek SPE-B (`Pd.01`/`Pd.05`/`Pd.02`).
|
|
150
|
+
- [`vesper/docs/README.md`](src/scietex/hal/vfd/vesper/docs/README.md) — Vesper
|
|
151
|
+
E5-8200 and E4-8400, plus the transport facts both manuals share.
|
|
152
|
+
|
|
153
|
+
## Contributing
|
|
154
|
+
|
|
155
|
+
Contributions are welcome! To contribute:
|
|
156
|
+
|
|
157
|
+
1. Fork the repository.
|
|
158
|
+
2. Create a feature branch (`git checkout -b feature/YourFeature`).
|
|
159
|
+
3. Commit your changes (`git commit -m "Add your message"`).
|
|
160
|
+
4. Push to the branch (`git push origin feature/YourFeature`).
|
|
161
|
+
5. Open a Pull Request.
|
|
162
|
+
|
|
163
|
+
Run the checks before opening a pull request:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
tox -e format,lint,type,test
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`format` and `lint` run `ruff`, `type` runs `ty`, and `test` runs `pytest` across
|
|
170
|
+
Python 3.10, 3.12 and 3.14.
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
This project is licensed under the **MIT License** - see the LICENSE file for details.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "scietex.hal.vfd"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Scietex VFD HAL package"
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Anton Bondarenko", email = "bond.anton@gmail.com" },
|
|
11
|
+
]
|
|
12
|
+
license = { file = "LICENSE" }
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
requires-python = ">=3.10"
|
|
20
|
+
dependencies = [
|
|
21
|
+
"msgspec[yaml] ~= 0.20.0",
|
|
22
|
+
"scietex.hal.serial >= 2.4.0, < 3.0.0",
|
|
23
|
+
"pymodbus ~= 3.15",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.entry-points."scietex.hal.vfd.drivers"]
|
|
27
|
+
intek_spe_b_modbus = "scietex.hal.vfd.intek.spe_b:IntekSPEBModbusVFD"
|
|
28
|
+
vesper_e5_8200_modbus = "scietex.hal.vfd.vesper.e5_8200:VesperE58200ModbusVFD"
|
|
29
|
+
vesper_e4_8400_modbus = "scietex.hal.vfd.vesper.e4_8400:VesperE48400ModbusVFD"
|
|
30
|
+
|
|
31
|
+
[project.entry-points."scietex.modbus.drivers"]
|
|
32
|
+
intek_spe_b = "scietex.hal.vfd.modbus_drivers:intek_spe_b"
|
|
33
|
+
vesper_e5_8200 = "scietex.hal.vfd.modbus_drivers:vesper_e5_8200"
|
|
34
|
+
vesper_e4_8400 = "scietex.hal.vfd.modbus_drivers:vesper_e4_8400"
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
"Homepage" = "https://github.com/bond-anton/scietex.hal.vfd"
|
|
38
|
+
"Bug Tracker" = "https://github.com/bond-anton/scietex.hal.vfd/issues"
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
dev = ["tox>=4.46.0"]
|
|
42
|
+
test = ["pytest", "pytest-asyncio", "pytest-xdist"]
|
|
43
|
+
lint = ["ruff", "ty"]
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.dynamic]
|
|
46
|
+
version = {attr = "scietex.hal.vfd.version.__version__"}
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.package-data]
|
|
49
|
+
"scietex.hal.vfd" = ["py.typed"]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["src"]
|
|
53
|
+
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
pythonpath = [
|
|
56
|
+
"src"
|
|
57
|
+
]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base VFD subpackage.
|
|
3
|
+
|
|
4
|
+
Provides the transport-agnostic driver contract, the capability model, the
|
|
5
|
+
shared data structures, and the RS485 base class the direct-serial drivers
|
|
6
|
+
derive from.
|
|
7
|
+
|
|
8
|
+
Classes:
|
|
9
|
+
VFD: Base class for drivers that own a serial connection.
|
|
10
|
+
VFDCapabilities: The control surface of one drive.
|
|
11
|
+
VFDError: A fault code paired with its message.
|
|
12
|
+
VFDParameters: A snapshot of the drive's operating parameters.
|
|
13
|
+
VFDProtocol: The contract every driver implements.
|
|
14
|
+
VFDState: The drive run state.
|
|
15
|
+
|
|
16
|
+
Modules:
|
|
17
|
+
protocol: Defines the `VFDProtocol` driver contract.
|
|
18
|
+
capabilities: Defines the `VFDCapabilities` model.
|
|
19
|
+
data: Defines the data structures shared by every driver.
|
|
20
|
+
rs485: Defines the `VFD` serial base class.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from .capabilities import VFDCapabilities
|
|
24
|
+
from .data import (
|
|
25
|
+
VFDClearErrorCMD,
|
|
26
|
+
VFDError,
|
|
27
|
+
VFDParameters,
|
|
28
|
+
VFDSetFrequencyCMD,
|
|
29
|
+
VFDStartCMD,
|
|
30
|
+
VFDState,
|
|
31
|
+
VFDStopCMD,
|
|
32
|
+
)
|
|
33
|
+
from .protocol import VFDProtocol
|
|
34
|
+
from .rs485 import VFD
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"VFD",
|
|
38
|
+
"VFDCapabilities",
|
|
39
|
+
"VFDClearErrorCMD",
|
|
40
|
+
"VFDError",
|
|
41
|
+
"VFDParameters",
|
|
42
|
+
"VFDProtocol",
|
|
43
|
+
"VFDSetFrequencyCMD",
|
|
44
|
+
"VFDStartCMD",
|
|
45
|
+
"VFDState",
|
|
46
|
+
"VFDStopCMD",
|
|
47
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VFD capability model.
|
|
3
|
+
|
|
4
|
+
A drive advertises what it can do through a single :class:`VFDCapabilities`
|
|
5
|
+
value. The fields are the operations a caller can dispatch without inspecting
|
|
6
|
+
the driver: whether the drive accepts a direction choice, a jog command, a
|
|
7
|
+
coasting stop, a fault reset, and a frequency setpoint.
|
|
8
|
+
|
|
9
|
+
The set is a frozen dataclass so a driver declares it once as a class attribute
|
|
10
|
+
and callers cannot mutate it.
|
|
11
|
+
|
|
12
|
+
Capabilities describe the *control* surface only. Whether a drive reports a
|
|
13
|
+
particular *reading* — a heatsink temperature, a DC bus voltage — is answered by
|
|
14
|
+
the corresponding :class:`scietex.hal.vfd.base.data.VFDParameters` field being
|
|
15
|
+
``None``, not by a flag here. One source of truth cannot drift from the driver.
|
|
16
|
+
|
|
17
|
+
Classes:
|
|
18
|
+
VFDCapabilities: The control surface of one drive.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True, slots=True)
|
|
25
|
+
class VFDCapabilities:
|
|
26
|
+
"""
|
|
27
|
+
The control surface of one drive.
|
|
28
|
+
|
|
29
|
+
Every field defaults to "unsupported", so a driver declares only what its
|
|
30
|
+
hardware exposes. Read-side operations (parameters, state, error code) are
|
|
31
|
+
mandatory for every driver and are therefore not advertised here.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
direction (bool): Whether the drive can be started in either rotation
|
|
35
|
+
direction. ``False`` means forward only.
|
|
36
|
+
jog (bool): Whether the drive accepts a jog command.
|
|
37
|
+
freewheel_stop (bool): Whether the drive can stop by coasting instead of
|
|
38
|
+
ramping down.
|
|
39
|
+
fault_reset (bool): Whether the drive accepts an explicit fault reset.
|
|
40
|
+
frequency_setpoint (bool): Whether the drive accepts a frequency
|
|
41
|
+
setpoint over the communication protocol, independent of the
|
|
42
|
+
keypad or an analog input.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
direction: bool = True
|
|
46
|
+
jog: bool = False
|
|
47
|
+
freewheel_stop: bool = False
|
|
48
|
+
fault_reset: bool = False
|
|
49
|
+
frequency_setpoint: bool = False
|