Compare commits

...

8 Commits

Author SHA1 Message Date
Zhang LongQi
3e01b476d3 3.0.0 2025-09-09 17:36:25 +08:00
Zhang LongQi
36c630d280 Update version to 0.3.0 in app.py 2025-09-09 17:32:28 +08:00
Zhang LongQi
ff00635b17 on/off use different time setting
- Changed Python version constraint from ">=3.9,<3.14" to ">=3.9,<3.13" in both files.
- Updated dependency versions in pyproject.toml to use wildcard "*" for development dependencies.
- Added hash values for minimalmodbus and pyserial in requirements.txt for enhanced security.
2025-09-09 17:32:01 +08:00
Zhang LongQi
51f6477462 Add favicon for the application 2025-06-06 10:59:16 +08:00
Zhang LongQi
e24444305f Update Python version constraints and enhance project dependencies
- Updated Python version requirement from ">=3.8,<4.0" to ">=3.9,<3.14" in pyproject.toml and requirements.txt.
- Added additional development dependencies: poetry, ipython, flake8, yapf, and pyinstaller.
- Configured multiple package sources for dependency resolution in pyproject.toml.
- Added formatting configurations for yapf and black to maintain code style consistency.
2025-06-06 10:37:00 +08:00
Zhang LongQi
77743885fe Enhance README with detailed usage instructions in Chinese and English; add README.pdf for additional documentation. 2025-06-05 18:28:41 +08:00
Zhang LongQi
3b228b0ced Update version to 0.2.0 in app.py and pyproject.toml 2025-06-05 18:18:05 +08:00
Zhang LongQi
034b83723c Add launch configuration for Python debugger and enhance app.py with charging cycle control 2025-06-05 17:52:45 +08:00
9 changed files with 2652 additions and 232 deletions

124
.gitignore vendored
View File

@ -1,2 +1,122 @@
app.dist/*
app.bin
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
devenv
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
.idea
.vscode
.DS_Store
venus/*
*.deb
*.deb.tar

17
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File with Arguments",
"type": "debugpy",
"request": "launch",
"program": "app.py",
"console": "integratedTerminal",
"args": "--interval 30 --repeat 2 --quiet COM14",
}
]
}

View File

@ -1,6 +1,94 @@
# How to use
# Xnergy Charger Control Demo
### English Instructions
The Xnergy Charger Control Demo is a tool designed to control Xnergy chargers via RS485 serial communication. This program allows users to set the charging time and repetition count for charging cycles, making it ideal for testing and demonstration purposes.
**Command Syntax**
```shell
xnergy-example.exe [-h] [--duration-on DURATION_ON] [--duration-off DURATION_OFF] [-r REPEAT] [-q] [-v] port
```
**Positional Arguments**
- **port** (Required): The serial port device (e.g., COM3, /dev/ttyUSB0) connected to the Xnergy RCU.
**Optional Arguments**
- `-h, --help`: Show this help message and exit.
- `--duration-on DURATION_ON`: Time (in seconds) to keep charging on. Default is 100 seconds.
- `--duration-off DURATION_OFF`: Time (in seconds) to keep charging off before next charging cycle. Default is 10 seconds.
- `-r REPEAT, --repeat REPEAT`: Number of times to repeat the charging cycle. Default is 1.
- `-q, --quiet`: Quiet mode. Suppress all debug messages.
- `-v, --version`: Show program version number and exit.
**Usage Examples**
1. Basic usage: `xnergy-example.exe COM3`
Controls the charger via COM3 using default duration settings and 1 repetition.
2. Custom durations and repeats: `xnergy-example.exe --duration-on 30 --duration-off 5 -r 5 COM5`
Controls the charger via COM5 with 30 seconds on, 5 seconds off, and 5 repetitions.
3. Quiet mode: `xnergy-example.exe -q COM5`
Runs on COM5 without showing debug messages.
### Xnergy 充电器控制演示程序说明
### 中文说明
Xnergy 充电器控制演示程序是一个用于控制 Xnergy 充电器的工具,通过 RS485 串口与充电器通信。该程序允许用户设置充电周期的持续时间和重复次数,适用于测试和演示场景。
**命令格式**
```
xnergy-example.exe [-h] [--duration-on DURATION_ON] [--duration-off DURATION_OFF] [-r REPEAT] [-q] [-v] port
```
**参数说明**
- **port**(必需):连接到 Xnergy RCU 的串口设备名称(如 COM3、/dev/ttyUSB0
**可选参数**
- `-h, --help`:显示此帮助信息并退出。
- `--duration-on DURATION_ON`:充电器开启的持续时间(秒),默认 100 秒。
- `--duration-off DURATION_OFF`:充电器关闭的持续时间(秒),默认 10 秒。
- `-r REPEAT, --repeat REPEAT`:充电周期的重复次数,默认 1 次。
- `-q, --quiet`:安静模式,抑制所有调试信息。
- `-v, --version`:显示程序版本号并退出。
**使用示例**
1. 基本用法:`xnergy-example.exe COM3`
通过 COM3 端口控制充电器,使用默认持续时间和 1 次重复。
2. 自定义持续时间和重复次数:`xnergy-example.exe --duration-on 60 --duration-off 15 -r 5 COM5`
通过 COM5 端口控制充电器,设置开启 60 秒,关闭 15 秒,重复 5 次。
3. 安静模式:`xnergy-example.exe -q COM5`
在 COM5 端口上运行,不显示调试信息。
### How to use code
Please use `python3`
* `pip3 install -r requirements.txt`
* `python3 app.py PORT`
- `pip3 install -r requirements.txt`
- `python3 app.py PORT`
### packaging
pyinstaller will be faster than nuitka, but sometimes the executable file generated by pyinstaller may be treated as a virus by some antivirus software.
```shell
poetry install --no-root --all-groups
```
```shell
poetry run nuitka --standalone --windows-icon-from-ico=".\xnergy_favicon.ico" .\app.py
```
```shell
poetry run pyinstaller --onefile --icon=".\xnergy_favicon.ico" .\app.py
```

BIN
README.pdf Normal file

Binary file not shown.

83
app.py
View File

@ -7,10 +7,10 @@ and show the charger voltage, battery voltage and charging current
stop charging in 30 seconds
'''
from minimalmodbus import Instrument, MODE_RTU
import serial
import sys
import time
VERSION = '0.1.0'
VERSION = '3.0.0'
UNIT = 16
MODBUS_FC_READ_SINGLE_COIL = int("0x01", 16)
@ -24,55 +24,62 @@ charger_voltage_adddress = 30
batery_voltage_address = 32
current_address = 31
def monitor_charger(instrument, interval=1, duration=30):
"""
Monitor the charger voltage, battery voltage and current for a given duration.
"""
t = 0
while t < duration:
try:
charger_voltage = instrument.read_registers(registeraddress=charger_voltage_adddress, number_of_registers=1, functioncode=MODBUS_FC_READ_INPUT_REGISTERS)
battery_voltage = instrument.read_registers(registeraddress=batery_voltage_address, number_of_registers=1, functioncode=MODBUS_FC_READ_INPUT_REGISTERS)
current = instrument.read_registers(registeraddress=current_address, number_of_registers=1, functioncode=MODBUS_FC_READ_INPUT_REGISTERS)
print(f'Time: {t}s - Charger Voltage: {charger_voltage[0]}({charger_voltage[0] / 128:.2f} V ) '
f'Battery Voltage: {battery_voltage[0]}({battery_voltage[0] / 128:.2f} V ) '
f'Current: {current[0]}({current[0] / 128:.2f} A )')
sys.stdout.flush()
except Exception as e:
print(f'Error reading registers: {e}')
pass
time.sleep(interval)
t += interval
print()
def run():
import argparse
parser = argparse.ArgumentParser(description='xnergy charger control demo')
parser.add_argument('port', type=str,
help='The serial port(RS485) that connect to xnergy RCU')
# parser.add_argument('-P', '--parallel', type=int, nargs='*', choices=(17, 18, 19),
# help='set to parallel mode, parameter is the unit id, can be used multiple times')
parser.add_argument('-v', '--verbose', action='count', default=0,
help='verbose mode, show more information')
parser.add_argument('port', type=str, help='The serial port(RS485) that connect to xnergy RCU')
parser.add_argument('--duration-on', type=int, default=100, help='The duration time in seconds to keep the charger on, default is 100 seconds')
parser.add_argument('--duration-off', type=int, default=10, help='The duration time in seconds to keep the charger off, default is 10 seconds')
parser.add_argument('-r', '--repeat', type=int, default=1, help='The number of times to repeat the charging cycle, default is 1')
parser.add_argument('-q', '--quiet', action='store_true', help='quiet mode, suppress all output')
parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {VERSION}')
args = parser.parse_args()
instrument = Instrument(port=args.port, mode=MODE_RTU, slaveaddress=UNIT, debug=True)
instrument = Instrument(port=args.port, mode=MODE_RTU, slaveaddress=UNIT, debug=not args.quiet)
instrument.serial.baudrate = 9600
# start charging
instrument.write_bit(registeraddress=enable_charger_address, value=1)
print('Start Charging, will stop charging in 30 seconds')
print(f'Start Charging, will start/stop charging {args.repeat} times with on duration {args.duration_on}s and off duration {args.duration_off}s')
counter = 0
while True:
charger_voltage = instrument.read_registers(registeraddress=charger_voltage_adddress, number_of_registers=1,
functioncode=MODBUS_FC_READ_INPUT_REGISTERS)
battery_voltage = instrument.read_registers(registeraddress=batery_voltage_address, number_of_registers=1,
functioncode=MODBUS_FC_READ_INPUT_REGISTERS)
current = instrument.read_registers(registeraddress=current_address, number_of_registers=1,
functioncode=MODBUS_FC_READ_INPUT_REGISTERS)
print(
str(counter) + ' '
'Charger voltage: ' + str(charger_voltage) + ' '
'Battery voltage: ' + str(battery_voltage) + ' '
'Current: ' + str(current)
, end='\r', flush=True)
while counter <= args.repeat:
# start charging
instrument.write_bit(registeraddress=enable_charger_address, value=1)
monitor_charger(instrument, duration=args.duration_on)
print()
# stop charging
instrument.write_bit(registeraddress=enable_charger_address, value=0)
monitor_charger(instrument, duration=args.duration_off)
print()
counter += 1
if counter > 30:
break
time.sleep(1)
print()
# stop charging
instrument.write_bit(registeraddress=enable_charger_address, value=0)
print('Stop Charging')
print(f'Charging cycle {counter} completed.')
if __name__ == "__main__":

2514
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,50 @@
[tool.poetry]
name = "xnergy-example"
version = "0.1.0"
version = "0.2.0"
description = "An example for Xnergy charger"
authors = ["longqi <longqi90@gmail.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = ">=3.8,<4.0"
python = ">=3.9,<3.13"
minimalmodbus = "^2.1.1"
[tool.poetry.group.dev.dependencies]
nuitka = "^2.7.2"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.requires-plugins]
poetry-plugin-export = "*"
poetry-plugin-shell = "*"
[tool.poetry.group.dev.dependencies]
nuitka = "*"
poetry = "*"
ipython = "*"
flake8 = "*"
yapf = "*"
pyinstaller = "*"
[[tool.poetry.source]]
name = "default"
url = "https://pypi.python.org/simple/"
priority = "primary"
[[tool.poetry.source]]
name = "aliyun"
url = "https://mirrors.aliyun.com/pypi/simple/"
priority = "supplemental"
[[tool.poetry.source]]
name = "tsinghua"
url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
priority = "supplemental"
[tool.yapf]
use_tabs = true
column_limit = 120
continuation_align_style = "valign-right"
[tool.black]
line-length = 120

View File

@ -1,2 +1,6 @@
minimalmodbus==2.1.1 ; python_version >= "3.8" and python_version < "4.0"
pyserial==3.5 ; python_version >= "3.8" and python_version < "4.0"
minimalmodbus==2.1.1 ; python_version >= "3.9" and python_version < "3.13" \
--hash=sha256:75c677e2f3ea901b762f8b2ab7cf8ad84de915bbea275d66e30b724e23887b1a \
--hash=sha256:c3f5a56e107d537e4bb420f7e735841ab2939c8ca6fb528f5fe4124571315b64
pyserial==3.5 ; python_version >= "3.9" and python_version < "3.13" \
--hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \
--hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0

BIN
xnergy_favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB