Weather station with ModBus over RS-485
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

618 lines
30 KiB

# Copyright 2021 Marcel Konstapel
# https://www.meezenest.nl/mees
#
# This file is part of SolarGeneratorDatalogger.
# A set of programs and scripts to log all the available data from a self build solar generator.
# It runs on a Raspberry Pi Zero 1 W with Raspbian
#
# It logs data from a EPEVER Soalrge Charge Controller (via Bluetooth) and an EPEVER solar charger (via RS485-USB).
# The data is analyzed and stored in a file. The data is also available via a web browser over Wifi
# (Raspberry Pi acts as access point).
# Future plan is to implement LoRa so the data can be accessed remotely.
#
# Program is configured via the file "config.json"
#
# Example of config_file:
#
# {
# "epever_data_file": "epever_data.json",
# "RS485_port": "/dev/ttyXRUSB0",
# "RS485_address": 1
# }
#
# Usage of program:
#
# -c, --config_file : reads the most important registers of the charge
# controller and writes it to the file specified in the
# config_file
# -s, --set_date : sets the date and time from the given argument with
# format: YYYY-MM-DD HH:MM:SS
# -l, --load : switches load on or off as given by the argument")
# -d, --dump_file : dumps all the registers in the specified file
#
#
# A lot of effort is made to catch all possible communication errors, so the program never hangs.
# When an error occurs, the program exits and has probably not written any data to 'epever_data_file'
# But at least we did not crash! Check the date of the data file to make sure the data is written or not.
#
# Example of epever_data_file (JSON format):
#
#{
# "solar_voltage": 0,
# "solar_current": 0,
# "solar_power": 0,
# "load_voltage": 13.11,
# "load_current": 0,
# "load_power": 0,
# "battery_voltage": 13.14,
# "battery_current": 0,
# "battery_power": 0,
# "controller_temp": 18.1,
# "ambient_temp": 16.42,
# "generated_energy_today": 0,
# "controller_date_time": "2021-11-12 15:56:52",
# "battery_status": {
# "wrong_identifaction_for_rated_voltage": false,
# "battery_inner_resistence_abnormal": false,
# "temperature_warning_status": "NORMAL",
# "battery_status": "NORMAL"
# },
# "controller_status": {
# "input_voltage_status": "NORMAL",
# "charging_mosfet_is_short_circuit": false,
# "charging_or_anti_reverse_mosfet_is_open_circuit": false,
# "anti_reverse_mosfet_is_short_circuit": false,
# "input_over_current": false,
# "load_over_current": false,
# "load_short_circuit": false,
# "load_mosfet_short_circuit": false,
# "disequilibrium_in_three_circuits": false,
# "pv_input_short_circuit": false,
# "charging_status": "NO_CHARGING",
# "fault": false,
# "running": true
# },
# "load_on": true
#}
#
# This code is based on:
# https://github.com/rosswarren/epevermodbus
# https://minimalmodbus.readthedocs.io/en/master/index.html
# https://github.com/kasbert/epsolar-tracer (Linux driver for CC-USB-RS485-150U USB to RS485 cable)
#
# Thanks for sharing your knowledge!
#
# V0.1 2021-11-12
# - First working program
# - Generic read works
# - Dump all registers works
# - Set date does not work yet
# - Make LOAD-switch availible through command line arguments
#
# V0.2 2021-11-15
# - Set date and time works
# - Load switch can now be set from command line
# - Started to implement programming of the controller, but stumbled across a problem: when the
# controller is in lithium mode, it seems like it is not possible to program some registers.
# This functionality is not documented by EPEVER. Bummer!
#
# This Python3 program gathers the data from the Liontron BMS over Bluetooth BLE.
#
# SolarGeneratorDatalogger is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SolarGeneratorDatalogger is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SolarGeneratorDatalogger. If not, see <https://www.gnu.org/licenses/>.
#
import json
import sys
import time
import getopt
from json.decoder import JSONDecodeError
from datetime import datetime
from itertools import count
from multiprocessing import Process
from epever_control import EpeverChargeController
# This function is very much a work in progress.
# It seems that is is not possible to program the registers when the controller is in lithium mode. This part of the protocol (lithium programming)
# is undocumented by EPEVER and no one in the community has figured it out yet. I could try to reverse engineer this, but for now I decided not to.
# The program as it is has all the functionality I need for my application, so that's all folk!
def dump_all_registers():
rawdat={}
#Address range 0x3000
rawdat['rated_solar_voltage'] = controller.get_rated_solar_voltage()
rawdat['rated_solar_current'] = controller.get_rated_solar_current()
rawdat['rated_solar_power'] = controller.get_rated_solar_power_l() + 256*controller.get_rated_solar_power_h()
rawdat['rated_charging_voltage'] = controller.get_rated_charging_voltage()
rawdat['rated_charging_current'] = controller.get_rated_charging_current()
rawdat['rated_charging_power'] = controller.get_rated_charging_power_l() + 256 * controller.get_rated_charging_power_h()
rawdat['charging_mode'] = controller.get_charging_mode()
rawdat['rated_load_current'] = controller.get_rated_load_current()
# Address range 0x3100
rawdat['solar_voltage'] = controller.get_solar_voltage()
rawdat['solar_current'] = controller.get_solar_current()
rawdat['solar_power'] = controller.get_solar_power_l() + 256 * controller.get_solar_power_h()
rawdat['battery_voltage'] = controller.get_battery_voltage()
rawdat['battery_current'] = controller.get_battery_current()
rawdat['battery_power'] = controller.get_battery_power_l() + 256 * controller.get_battery_power_h()
rawdat['load_voltage'] = controller.get_load_voltage()
rawdat['load_current'] = controller.get_load_current()
rawdat['load_power'] = controller.get_load_power_l() + 256 * controller.get_load_power_h()
rawdat['battery_temperature'] = controller.get_battery_temperature()
rawdat['controller_temperature'] = controller.get_controller_temperature()
rawdat['heatsink_temperature'] = controller.get_heatsink_temperature()
rawdat['battery_state_of_charge'] = controller.get_battery_state_of_charge()
rawdat['remote_battery_temperature'] = controller.get_remote_battery_temperature()
rawdat['battery_real_rated_voltage'] = controller.get_battery_real_rated_voltage()
# Address range 0x3200
rawdat['battery_status'] = controller.get_battery_status()
rawdat['charging_equipment_status'] = controller.get_charging_equipment_status()
# Address 0x3300
rawdat['maximum_solar_voltage_today'] = controller.get_maximum_solar_voltage_today()
rawdat['minimum_solar_voltage_today'] = controller.get_minimum_solar_voltage_today()
rawdat['maximum_battery_voltage_today'] = controller.get_maximum_battery_voltage_today()
rawdat['minimum_battery_voltage_today'] = controller.get_minimum_battery_voltage_today()
rawdat['consumed_energy_today'] = controller.get_consumed_energy_today_l() + 256 * controller.get_consumed_energy_today_h()
rawdat['consumed_energy_month'] = controller.get_consumed_energy_month_l() + 256 * controller.get_consumed_energy_month_h()
rawdat['consumed_energy_year'] = controller.get_consumed_energy_year_l() + 256 * controller.get_consumed_energy_year_h()
rawdat['consumed_energy_total'] = controller.get_consumed_energy_total_l() + 256 * controller.get_consumed_energy_total_h()
rawdat['generated_energy_today'] = controller.get_generated_energy_today_l() + 256 * controller.get_generated_energy_today_h()
rawdat['generated_energy_month'] = controller.get_generated_energy_month_l() + 256 * controller.get_generated_energy_month_h()
rawdat['generated_energy_year'] = controller.get_generated_energy_year_l() + 256 * controller.get_generated_energy_year_h()
rawdat['generated_energy_total'] = controller.get_generated_energy_total_l() + 256 * controller.get_generated_energy_total_h()
rawdat['carbon_reduction'] = controller.get_carbon_reduction_l() + 256 * controller.get_carbon_reduction_h()
rawdat['battery_net_current'] = controller.get_battery_net_current_l() + 256 * controller.get_battery_net_current_h()
rawdat['battery_temperature'] = controller.get_battery_temperature()
rawdat['ambient_temperature'] = controller.get_ambient_temperature()
# Address range 0x9000
rawdat['battery_type'] = controller.get_battery_type()
rawdat['battery_capacity'] = controller.get_battery_capacity()
rawdat['temperature_compensation_coefficient'] = controller.get_temperature_compensation_coefficient()
rawdat['over_voltage_disconnect_voltage'] = controller.get_over_voltage_disconnect_voltage()
rawdat['charging_limit_voltage'] = controller.get_charging_limit_voltage()
rawdat['over_voltage_reconnect_voltage'] = controller.get_over_voltage_reconnect_voltage()
rawdat['equalize_charging_voltage'] = controller.get_equalize_charging_voltage()
rawdat['boost_charging_voltage'] = controller.get_boost_charging_voltage()
rawdat['float_charging_voltage'] = controller.get_float_charging_voltage()
rawdat['boost_reconnect_charging_voltage'] = controller.get_boost_reconnect_charging_voltage()
rawdat['low_voltage_reconnect_voltage'] = controller.get_low_voltage_reconnect_voltage()
rawdat['under_voltage_recover_voltage'] = controller.get_under_voltage_recover_voltage()
rawdat['under_voltage_warning_voltage'] = controller.get_under_voltage_warning_voltage()
rawdat['low_voltage_disconnect_voltage'] = controller.get_low_voltage_disconnect_voltage()
rawdat['discharging_limit_voltage'] = controller.get_discharging_limit_voltage()
rawdat['controller_date_time'] = controller.get_datetime()
rawdat['equalization_charging_cycle'] = controller.get_equalization_charging_cycle()
rawdat['battery_temp_upper_warning_limit'] = controller.get_battery_temp_upper_warning_limit()
rawdat['battery_temp_lower_warning_limit'] = controller.get_battery_temp_lower_warning_limit()
rawdat['controller_inner_temp_upper_limit'] = controller.get_controller_inner_temp_upper_limit()
rawdat['controller_inner_temp_upper_limit_recover'] = controller.get_controller_inner_temp_upper_limit_recover()
rawdat['power_comp_temp_upper_limit'] = controller.get_power_comp_temp_upper_limit()
rawdat['power_comp_temp_upper_limit_recover'] = controller.get_power_comp_temp_upper_limit_recover()
rawdat['line_impedance'] = controller.get_line_impedance()
rawdat['night_time_threshold_volt'] = controller.get_night_time_threshold_volt()
rawdat['light_signal_startup_delay'] = controller.get_light_signal_startup_delay()
rawdat['day_time_threshold_volt'] = controller.get_day_time_threshold_volt()
rawdat['light_signal_turnoff_delay'] = controller.get_light_signal_turnoff_delay()
rawdat['controling_mode'] = controller.get_controling_mode()
rawdat['working_time_lenght_1'] = controller.get_working_time_lenght_1()
rawdat['working_time_lenght_2'] = controller.get_working_time_lenght_2()
rawdat['turn_on_time_1_sec'] = controller.get_turn_on_time_1_sec()
rawdat['turn_on_time_1_min'] = controller.get_turn_on_time_1_min()
rawdat['turn_on_time_1_hours'] = controller.get_turn_on_time_1_hours()
rawdat['turn_off_time_1_sec'] = controller.get_turn_off_time_1_sec()
rawdat['turn_off_time_1_min'] = controller.get_turn_off_time_1_min()
rawdat['turn_off_time_1_hours'] = controller.get_turn_off_time_1_hours()
rawdat['turn_on_time_2_sec'] = controller.get_turn_on_time_2_sec()
rawdat['turn_on_time_2_min'] = controller.get_turn_on_time_2_min()
rawdat['turn_on_time_2_hours'] = controller.get_turn_on_time_2_hours()
rawdat['turn_off_time_2_sec'] = controller.get_turn_off_time_2_sec()
rawdat['turn_off_time_2_min'] = controller.get_turn_off_time_2_min()
rawdat['turn_off_time_2_hours'] = controller.get_turn_off_time_2_hours()
rawdat['length_of_night'] = controller.get_length_of_night()
rawdat['battery_rated_voltage'] = controller.get_battery_rated_voltage()
rawdat['load_timing_control_selection'] = controller.get_load_timing_control_selection()
rawdat['default_load_on_off_in_manual_mode'] = controller.get_default_load_on_off_in_manual_mode()
rawdat['equalize_duration'] = controller.get_equalize_duration()
rawdat['boost_duration'] = controller.get_boost_duration()
rawdat['battery_discharge'] = controller.get_battery_discharge()
rawdat['battery_charge'] = controller.get_battery_charge()
rawdat['management_mode'] = controller.get_management_mode()
# Address range 0x0
rawdat['manual_control'] = controller.get_manual_control()
rawdat['enable_load_test_mode'] = controller.get_enable_load_test_mode()
rawdat['force_load_on'] = controller.get_force_load_on()
# Address range 0x2000
rawdat['is_device_over_temperature'] = controller.is_device_over_temperature()
rawdat['is_night'] = controller.is_night()
print (json.dumps(rawdat, indent=1, sort_keys=False))
with open(dump_file, 'w') as f:
json.dump(rawdat, f, indent=1)
# Reads the given config-file and programs all the registers defined in this file to the solar controller
def program_registers():
print("Program the registers specified in the register file:", register_file)
# read configuration file
try:
with open(register_file, 'r') as myfile:
register_data=myfile.read()
except FileNotFoundError:
print("Configuration file <"+ register_file +"> not found.")
sys.exit();
# parse configuration file
try:
register_obj = json.loads(register_data)
except JSONDecodeError as e:
print("Error decoding configuration file. Not JSON format?")
sys.exit()
except TypeError as e:
print("Error decoding configuration file: JSON objects not str, bytes or bytearray.")
sys.exit()
except KeyError:
print("Error reading configuration file: needed fields not found.")
sys.exit()
if 'battery_type' in register_obj:
#controller.set_battery_type(register_obj['battery_type']):
print("battery_type =", register_obj['battery_type'])
if 'battery_capacity' in register_obj:
#controller.set_battery_capacity(register_obj['battery_capacity'])
print("battery_capacity =", register_obj['battery_capacity'])
if 'temperature_compensation_coefficient' in register_obj:
#controller.set_temperature_compensation_coefficient(register_obj['temperature_compensation_coefficient'])
print("temperature_compensation_coefficient =", register_obj['temperature_compensation_coefficient'])
if 'over_voltage_disconnect_voltage' in register_obj:
#controller.set_over_voltage_disconnect_voltage(register_obj['over_voltage_disconnect_voltage'])
print("over_voltage_disconnect_voltage =", register_obj['over_voltage_disconnect_voltage'])
if 'charging_limit_voltage' in register_obj:
#controller.set_charging_limit_voltage(register_obj['charging_limit_voltage'])
print("charging_limit_voltage =", register_obj['charging_limit_voltage'])
if 'over_voltage_reconnect_voltage' in register_obj:
#controller.set_over_voltage_reconnect_voltage(register_obj['charging_limit_voltage'])
print("over_voltage_reconnect_voltage =", register_obj['over_voltage_reconnect_voltage'])
if 'equalize_charging_voltage' in register_obj:
#controller.set_equalize_charging_voltage(register_obj['charging_limit_voltage'])
print("equalize_charging_voltage =", register_obj['equalize_charging_voltage'])
if 'boost_charging_voltage' in register_obj:
#controller.set_boost_charging_voltage(register_obj['charging_limit_voltage'])
print("boost_charging_voltage =", register_obj['boost_charging_voltage'])
if 'float_charging_voltage' in register_obj:
#controller.set_float_charging_voltage(register_obj['charging_limit_voltage'])
print("float_charging_voltage =", register_obj['float_charging_voltage'])
if 'boost_reconnect_charging_voltage' in register_obj:
#controller.set_boost_reconnect_charging_voltage(register_obj['charging_limit_voltage'])
print("boost_reconnect_charging_voltage =", register_obj['boost_reconnect_charging_voltage'])
if 'low_voltage_reconnect_voltage' in register_obj:
#controller.set_low_voltage_reconnect_voltage(register_obj['charging_limit_voltage'])
print("low_voltage_reconnect_voltage =", register_obj['low_voltage_reconnect_voltage'])
if 'under_voltage_recover_voltage' in register_obj:
#controller.set_under_voltage_recover_voltage(register_obj['charging_limit_voltage'])
print("under_voltage_recover_voltage =", register_obj['under_voltage_recover_voltage'])
if 'under_voltage_warning_voltage' in register_obj:
#controller.set_under_voltage_warning_voltage(register_obj['charging_limit_voltage'])
print("under_voltage_warning_voltage =", register_obj['under_voltage_warning_voltage'])
if 'low_voltage_disconnect_voltage' in register_obj:
#controller.set_low_voltage_disconnect_voltage(register_obj['charging_limit_voltage'])
print("low_voltage_disconnect_voltage =", register_obj['low_voltage_disconnect_voltage'])
if 'discharging_limit_voltage' in register_obj:
#controller.set_discharging_limit_voltage(register_obj['charging_limit_voltage'])
print("discharging_limit_voltage =", register_obj['discharging_limit_voltage'])
if 'datetime_sec_min' in register_obj:
#controller.set_datetime_sec_min(self, value):
print("datetime_sec_min =", register_obj['datetime_sec_min'])
if 'datetime_hour_day' in register_obj:
#controller.set_datetime_hour_day(self, value):
print("datetime_hour_day =", register_obj['datetime_hour_day'])
if 'datetime_month_year' in register_obj:
#controller.set_datetime_month_year(self, value):
print("datetime_month_year =", register_obj['datetime_month_year'])
if 'equalization_charging_cycle' in register_obj:
#controller.set_equalization_charging_cycle(self, value):
print("equalization_charging_cycle =", register_obj['equalization_charging_cycle'])
if 'battery_temp_upper_warning_limit' in register_obj:
#controller.set_battery_temp_upper_warning_limit(self, value):
print("battery_temp_upper_warning_limit =", register_obj['battery_temp_upper_warning_limit'])
if 'battery_temp_lower_warning_limit' in register_obj:
#controller.set_battery_temp_lower_warning_limit(self, value):
print("battery_temp_lower_warning_limit =", register_obj['battery_temp_lower_warning_limit'])
if 'controller_inner_temp_upper_limit' in register_obj:
#controller.set_controller_inner_temp_upper_limit(self, value):
print("controller_inner_temp_upper_limit =", register_obj['controller_inner_temp_upper_limit'])
if 'controller_inner_temp_upper_limit_recover' in register_obj:
#controller.set_controller_inner_temp_upper_limit_recover(self, value):
print("controller_inner_temp_upper_limit_recover =", register_obj['controller_inner_temp_upper_limit_recover'])
if 'power_comp_temp_upper_limit' in register_obj:
#controller.set_power_comp_temp_upper_limit(self, value):
print("equalization_charging_cycle =", register_obj['equalization_charging_cycle'])
if 'power_comp_temp_upper_limit_recover' in register_obj:
#controller.set_power_comp_temp_upper_limit_recover(self, value):
print("power_comp_temp_upper_limit_recover =", register_obj['power_comp_temp_upper_limit_recover'])
if 'line_impedance' in register_obj:
#controller.set_line_impedance(self, value):
print("line_impedance =", register_obj['line_impedance'])
if 'night_time_threshold_volt' in register_obj:
#controller.set_night_time_threshold_volt(self, value):
print("night_time_threshold_volt =", register_obj['night_time_threshold_volt'])
if 'light_signal_startup_delay' in register_obj:
#controller.set_light_signal_startup_delay(self, value):
print("light_signal_startup_delay =", register_obj['light_signal_startup_delay'])
if 'day_time_threshold_volt' in register_obj:
#controller.set_day_time_threshold_volt(self, value):
print("day_time_threshold_volt =", register_obj['day_time_threshold_volt'])
if 'light_signal_turnoff_delay' in register_obj:
#controller.set_light_signal_turnoff_delay(self, value)
print("light_signal_turnoff_delay =", register_obj['light_signal_turnoff_delay'])
if 'controling_mode' in register_obj:
#controller.set_controling_mode(self, value):
print("controling_mode =", register_obj['controling_mode'])
if 'working_time_lenght_1' in register_obj:
#controller.set_working_time_lenght_1(self, value):
print("working_time_lenght_1 =", register_obj['working_time_lenght_1'])
if 'working_time_lenght_2' in register_obj:
#controller.set_working_time_lenght_2(self, value):
print("working_time_lenght_2 =", register_obj['working_time_lenght_2'])
if 'turn_on_time_1_sec' in register_obj:
#controller.set_turn_on_time_1_sec(self, value):
print("turn_on_time_1_sec =", register_obj['turn_on_time_1_sec'])
if 'turn_on_time_1_min' in register_obj:
#controller.set_turn_on_time_1_min(self, value):
print("turn_on_time_1_min =", register_obj['turn_on_time_1_min'])
if 'turn_on_time_1_hours' in register_obj:
#controller.set_turn_on_time_1_hours(self, value):
print("turn_on_time_1_hours =", register_obj['turn_on_time_1_hours'])
if 'turn_off_time_1_sec' in register_obj:
#controller.set_turn_off_time_1_sec(self, value):
print("turn_off_time_1_sec =", register_obj['turn_off_time_1_sec'])
if 'turn_off_time_1_min' in register_obj:
#controller.set_turn_off_time_1_min(self, value):
print("turn_off_time_1_min =", register_obj['turn_off_time_1_min'])
if 'turn_off_time_1_hours' in register_obj:
#controller.set_turn_off_time_1_hours(self, value):
print("turn_off_time_1_hours =", register_obj['turn_off_time_1_hours'])
if 'turn_on_time_2_sec' in register_obj:
#controller.set_turn_on_time_2_sec(self, value):
print("turn_on_time_2_sec =", register_obj['turn_on_time_2_sec'])
if 'turn_on_time_2_min' in register_obj:
#controller.set_turn_on_time_2_min(self, value):
print("turn_on_time_2_min =", register_obj['turn_on_time_2_min'])
if 'turn_on_time_2_hours' in register_obj:
#controller.set_turn_on_time_2_hours(self, value):
print("turn_on_time_2_hours =", register_obj['turn_on_time_2_hours'])
if 'turn_off_time_2_sec' in register_obj:
#controller.set_turn_off_time_2_sec(self, value):
print("turn_off_time_2_sec =", register_obj['turn_off_time_2_sec'])
if 'turn_off_time_2_min' in register_obj:
#controller.set_turn_off_time_2_min(self, value):
print("turn_off_time_2_min =", register_obj['turn_off_time_2_min'])
if 'turn_off_time_2_hours' in register_obj:
#controller.set_turn_off_time_2_hours(self, value):
print("turn_off_time_2_hours =", register_obj['turn_off_time_2_hours'])
if 'length_of_night' in register_obj:
#controller.set_length_of_night(self, value):
print("length_of_night =", register_obj['length_of_night'])
if 'battery_rated_voltage' in register_obj:
#controller.set_battery_rated_voltage(self, value):
print("battery_rated_voltage =", register_obj['battery_rated_voltage'])
if 'load_timing_control_selection' in register_obj:
#controller.set_load_timing_control_selection(self, value):
print("load_timing_control_selection =", register_obj['load_timing_control_selection'])
if 'default_load_on_off_in_manual_mode' in register_obj:
#controller.set_default_load_on_off_in_manual_mode(self, value):
print("default_load_on_off_in_manual_mode =", register_obj['default_load_on_off_in_manual_mode'])
if 'equalize_duration' in register_obj:
#controller.set_equalize_duration(self, value):
print("equalize_duration =", register_obj['equalize_duration'])
if 'boost_duration' in register_obj:
#controller.set_boost_duration(self, value):
print("boost_duration =", register_obj['boost_duration'])
if 'battery_discharge' in register_obj:
#controller.set_battery_discharge(self, value):
print("battery_discharge =", register_obj['battery_discharge'])
if 'battery_charge' in register_obj:
#controller.set_battery_charge(self, value):
print("battery_charge =", register_obj['battery_charge'])
if 'management_mode' in register_obj:
#controller.set_management_mode(self, value):
print("management_mode =", register_obj['management_mode'])
print("This functionality is not implemented yet. Nothing is actually written to the solar charger.")
config_file =""
set_date =""
load_switch=""
register_file=""
dump_file=""
argv = sys.argv[1:]
try:
options, args = getopt.getopt(argv, "c:s:l:p:d:",
["config_file =",
"set_date =",
"load_switch =",
"register_file=",
"dump_file ="])
except:
print("epever-charge-controller can read and write the registers of a EPEVER solar")
print("charge controller using the Modbus protocol.")
print("")
print("-c, --config_file : reads the most important registers of the charge")
print(" controller and writes it to the file specified in the")
print(" config_file")
print("-s, --set_date : sets the date and time from the given argument with")
print(" format: YYYY-MM-DD HH:MM:SS")
print("-l, --load : switches load on or off as given by the argument")
print("-p, --program : programs all the registers of the solar charger given in")
print(" the specified register file.")
print("-d, --dump_file : dumps all the registers in the specified file")
sys.exit()
for name, value in options:
if name in ['-c', '--config_file']:
config_file = value
elif name in ['-s', '--set_date']:
set_date = value
elif name in ['-l', '--load']:
load_switch = value
elif name in ['-p', '--program']:
register_file = value
elif name in ['-d', '--dump']:
dump_file = value
if not config_file:
print("No configuration file given.")
sys.exit();
# read configuration file
try:
with open(config_file, 'r') as myfile:
data=myfile.read()
except FileNotFoundError:
print("Configuration file <"+ config_file +"> not found.")
sys.exit();
# parse configuration file
try:
obj = json.loads(data)
tmp = obj['epever_data_file']
tmp1 = obj['RS485_port']
tmp2 = obj['RS485_address']
except JSONDecodeError as e:
print("Error decoding configuration file. Not JSON format?")
sys.exit()
except TypeError as e:
print("Error decoding configuration file: JSON objects not str, bytes or bytearray.")
sys.exit()
except KeyError:
print("Error reading configuration file: needed fields not found.")
sys.exit()
# show values from config file
print("Using RRS485 port : " + str(obj['RS485_port']))
print("Using RRS485 address : " + str(obj['RS485_address']))
print("Saving EPEVER data to: " + str(obj['epever_data_file']))
controller = EpeverChargeController(obj['RS485_port'], obj['RS485_address'])
if set_date:
try:
iso_date = datetime.fromisoformat(set_date)
except:
print("Error in date and time. Date and time should be in ISO format.")
sys.exit()
print("Setting the time and date to", iso_date)
datetime={}
datetime['year'] = int(iso_date.strftime('%Y')) - 2000
datetime['month'] = int(iso_date.strftime('%m'))
datetime['day'] = int(iso_date.strftime('%d'))
datetime['hours'] = int(iso_date.strftime('%H'))
datetime['minutes'] = int(iso_date.strftime('%M'))
datetime['seconds'] = int(iso_date.strftime('%S'))
controller.write_date(datetime)
elif load_switch:
if (load_switch.lower()=='on'):
print("Switching load ON.")
controller.switch_load_on()
elif (load_switch.lower()=='off'):
print("Switching load OFF.")
controller.switch_load_off()
else:
print("No argument given. Cannot switch load.")
elif register_file:
program_registers();
elif dump_file:
print("Dumping all registers to file: "+ dump_file)
dump_all_registers()
else:
status = 1
print("Enable heater function")
controller.enable_heater()
while (1):
time.sleep(3) # Sleep for 3 seconds
print ("Retrieving all known registers.")
rawdat={}
rawdat['ID'] = controller.get_id()
rawdat['Wind direction'] = controller.get_wind_direction()
rawdat['Wind speed'] = controller.get_wind_speedl()
rawdat['Wind gust'] = controller.get_wind_gust()
rawdat['Rain last hour'] = controller.get_rain()
rawdat['Rain last 24 hours'] = controller.get_rain_last24()
rawdat['Temperature'] = controller.get_temperature()
rawdat['Humidity'] = controller.get_humidity()
rawdat['Pressure'] = controller.get_pressure()
rawdat['Temp backup'] = controller.get_temperature_backup()
rawdat['Status bits'] = controller.get_status_bits()
print (json.dumps(rawdat, indent=1, sort_keys=False))