Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update solar.py #2316

Merged
merged 3 commits into from
Sep 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 73 additions & 42 deletions examples/contrib/solar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

Modified to test long term connection.

Modified to actually work with Huawei SUN2000 inverters, that better support async Modbus operations so errors will occur
Configure HOST (the IP address of the inverter as a string), PORT and CYCLES to fit your needs

"""
import logging
from enum import Enum
from time import sleep

from pymodbus import pymodbus_apply_logging_config
Expand All @@ -14,70 +18,97 @@
# --------------------------------------------------------------------------- #
from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ModbusException
from pymodbus.transaction import ModbusSocketFramer
from pymodbus.pdu import ExceptionResponse
from pymodbus import FramerType


HOST = "modbusServer.lan"
PORT = 502
CYCLES = 4


pymodbus_apply_logging_config(logging.ERROR)
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(message)s')
_logger = logging.getLogger(__file__)
_logger.setLevel(logging.DEBUG)


def main():
def main() -> None:
"""Run client setup."""
pymodbus_apply_logging_config(logging.DEBUG)
_logger.info("### Client starting")
client = ModbusTcpClient(
"modbusServer.lan",
port=502,
client: ModbusTcpClient = ModbusTcpClient(
host=HOST,
port=PORT,
# Common optional parameters:
framer=ModbusSocketFramer,
framer=FramerType.SOCKET,
timeout=1,
)
client.connect()
_logger.info("### Client connected")
sleep(5)
sleep(1)
_logger.info("### Client starting")
sleep_time = 2
for count in range(int(60 / sleep_time) * 60 * 3): # 3 hours
for count in range(CYCLES):
_logger.info(f"Running loop {count}")
solar_calls(client)
sleep(sleep_time) # scan_interval
client.close()
_logger.info("### End of Program")


def solar_calls(client):
"""Test connection works."""
for addr, count in (
(32008, 1),
(32009, 1),
(32010, 1),
(32016, 1),
(32017, 1),
(32018, 1),
(32019, 1),
(32064, 2),
(32078, 2),
(32080, 2),
(32114, 2),
(37113, 2),
(32078, 2),
(32078, 2),
def solar_calls(client: ModbusTcpClient) -> None:
"""Read registers."""
error = False

for addr, format, factor, comment, unit in ( # data_type according to ModbusClientMixin.DATATYPE.value[0]
(32008, "H", 1, "Alarm 1", "(bitfield)"),
(32009, "H", 1, "Alarm 2", "(bitfield)"),
(32010, "H", 1, "Alarm 3", "(bitfield)"),
(32016, "h", 0.1, "PV 1 voltage", "V"),
(32017, "h", 0.01, "PV 1 current", "A"),
(32018, "h", 0.1, "PV 2 voltage", "V"),
(32019, "h", 0.01, "PV 2 current", "A"),
(32064, "i", 0.001, "Input power", "kW"),
(32078, "i", 0.001, "Peak active power of current day", "kW"),
(32080, "i", 0.001, "Active power", "kW"),
(37114, "I", 0.001, "Daily energy yield", "kW"),
):
lazy_error_count = 15
while lazy_error_count > 0:
try:
rr = client.read_coils(addr, count, slave=1)
except ModbusException as exc:
_logger.debug(f"TEST: exception lazy({lazy_error_count}) {exc}")
lazy_error_count -= 1
continue
if not hasattr(rr, "registers"):
_logger.debug(f"TEST: no registers lazy({lazy_error_count})")
lazy_error_count -= 1
continue
break
if not lazy_error_count:
raise RuntimeError("HARD ERROR, more than 15 retries!")
if error:
error = False
client.close()
sleep(0.1)
client.connect()
sleep(1)

data_type = get_data_type(format)
count = data_type.value[1]

_logger.info(f"*** Reading {comment}")

try:
rr = client.read_holding_registers(address=addr, count=count, slave=1)
except ModbusException as exc:
_logger.error(f"Modbus exception: {exc!s}")
error = True
continue
if rr.isError():
_logger.error(f"Error")
error = True
continue
if isinstance(rr, ExceptionResponse):
_logger.error(f"Response exception: {rr!s}")
error = True
continue

value = client.convert_from_registers(rr.registers, data_type) * factor
_logger.info(f"*** READ *** {comment} = {value} {unit}")


def get_data_type(format: str) -> Enum:
"""Return the ModbusTcpClient.DATATYPE according to the format"""
for data_type in ModbusTcpClient.DATATYPE:
if data_type.value[0] == format:
return data_type


if __name__ == "__main__":
Expand Down