First commit
This commit is contained in:
35
REAMDE.md
Normal file
35
REAMDE.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Weather Station MK2
|
||||
|
||||
The revised version of the Mees Electronics Weather Station
|
||||
|
||||
# New design philosophy
|
||||
|
||||
## Modular
|
||||
|
||||
Each sensor has its own RS485 connection. Sensors are daisy chained and the system is therefore expandable.
|
||||
|
||||
## Interface
|
||||
|
||||
The Weipu SP1312 / S 4, a 4 pole connector with an IP68 rating can be used as the physical interface. Each sensor has two identical connectors, for easy daisy chaining. connectors which are not used can be capped off with a weather proof cap. The cables use the Weipu SP1310 / P 4 connector.
|
||||
|
||||
## ModBus
|
||||
|
||||
Each sensor has a bus address, which can be set by DIP switches. Input registers store sensor data while output coils can be used to send commands to the sensors.
|
||||
|
||||
## Main controller
|
||||
|
||||
All sensors are connected via an RS485 bus to the main controller, the client. This unit collects the sensor data and publishes the data on a tcp/ip network via MQTT and a web interface. This unit can also function as a data logger. The data logger data can be accessed via a simple API.
|
||||
|
||||
The main controller has to know which sensors are connected. The ModBus addresses of all the sensors are stored in a configuration file. The client software can now poll these addresses and discover the sensors.
|
||||
|
||||
# Links
|
||||
|
||||
Useful resources:
|
||||
|
||||
[https://github.com/danjperron/rp2040-modbus_example/tree/main](https://github.com/danjperron/rp2040-modbus_example/tree/main)
|
||||
|
||||
# Licensing
|
||||
|
||||
(C) 2025 Marcel Konstapel [https://www.meezenest.nl/mees/](https://www.meezenest.nl/mees/)
|
||||
|
||||
Software under GPL, hardware and software under CC BY-SA 4.0.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2
hardware/sensor_temperature/sensor_temperature.kicad_pcb
Normal file
2
hardware/sensor_temperature/sensor_temperature.kicad_pcb
Normal file
@@ -0,0 +1,2 @@
|
||||
(kicad_pcb (version 20211014) (generator pcbnew)
|
||||
)
|
75
hardware/sensor_temperature/sensor_temperature.kicad_prl
Normal file
75
hardware/sensor_temperature/sensor_temperature.kicad_prl
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"board": {
|
||||
"active_layer": 0,
|
||||
"active_layer_preset": "",
|
||||
"auto_track_width": true,
|
||||
"hidden_nets": [],
|
||||
"high_contrast_mode": 0,
|
||||
"net_color_mode": 1,
|
||||
"opacity": {
|
||||
"pads": 1.0,
|
||||
"tracks": 1.0,
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"ratsnest_display_mode": 0,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
"graphics": true,
|
||||
"keepouts": true,
|
||||
"lockedItems": true,
|
||||
"otherItems": true,
|
||||
"pads": true,
|
||||
"text": true,
|
||||
"tracks": true,
|
||||
"vias": true,
|
||||
"zones": true
|
||||
},
|
||||
"visible_items": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36
|
||||
],
|
||||
"visible_layers": "fffffff_ffffffff",
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"meta": {
|
||||
"filename": "sensor_temperature.kicad_prl",
|
||||
"version": 3
|
||||
},
|
||||
"project": {
|
||||
"files": []
|
||||
}
|
||||
}
|
326
hardware/sensor_temperature/sensor_temperature.kicad_pro
Normal file
326
hardware/sensor_temperature/sensor_temperature.kicad_pro
Normal file
@@ -0,0 +1,326 @@
|
||||
{
|
||||
"board": {
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"board_outline_line_width": 0.1,
|
||||
"copper_line_width": 0.2,
|
||||
"copper_text_size_h": 1.5,
|
||||
"copper_text_size_v": 1.5,
|
||||
"copper_text_thickness": 0.3,
|
||||
"other_line_width": 0.15,
|
||||
"silk_line_width": 0.15,
|
||||
"silk_text_size_h": 1.0,
|
||||
"silk_text_size_v": 1.0,
|
||||
"silk_text_thickness": 0.15
|
||||
},
|
||||
"diff_pair_dimensions": [],
|
||||
"drc_exclusions": [],
|
||||
"rules": {
|
||||
"min_copper_edge_clearance": 0.0,
|
||||
"solder_mask_clearance": 0.0,
|
||||
"solder_mask_min_width": 0.0
|
||||
},
|
||||
"track_widths": [],
|
||||
"via_dimensions": []
|
||||
},
|
||||
"layer_presets": []
|
||||
},
|
||||
"boards": [],
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
"erc": {
|
||||
"erc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"pin_map": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
]
|
||||
],
|
||||
"rule_severities": {
|
||||
"bus_definition_conflict": "error",
|
||||
"bus_entry_needed": "error",
|
||||
"bus_label_syntax": "error",
|
||||
"bus_to_bus_conflict": "error",
|
||||
"bus_to_net_conflict": "error",
|
||||
"different_unit_footprint": "error",
|
||||
"different_unit_net": "error",
|
||||
"duplicate_reference": "error",
|
||||
"duplicate_sheet_names": "error",
|
||||
"extra_units": "error",
|
||||
"global_label_dangling": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"label_dangling": "error",
|
||||
"lib_symbol_issues": "warning",
|
||||
"multiple_net_names": "warning",
|
||||
"net_not_bus_member": "warning",
|
||||
"no_connect_connected": "warning",
|
||||
"no_connect_dangling": "warning",
|
||||
"pin_not_connected": "error",
|
||||
"pin_not_driven": "error",
|
||||
"pin_to_pin": "warning",
|
||||
"power_pin_not_driven": "error",
|
||||
"similar_labels": "warning",
|
||||
"unannotated": "error",
|
||||
"unit_value_mismatch": "error",
|
||||
"unresolved_variable": "error",
|
||||
"wire_dangling": "error"
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"pinned_footprint_libs": [],
|
||||
"pinned_symbol_libs": []
|
||||
},
|
||||
"meta": {
|
||||
"filename": "sensor_temperature.kicad_pro",
|
||||
"version": 1
|
||||
},
|
||||
"net_settings": {
|
||||
"classes": [
|
||||
{
|
||||
"bus_width": 12.0,
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Default",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.25,
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 6.0
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 2
|
||||
},
|
||||
"net_colors": null
|
||||
},
|
||||
"pcbnew": {
|
||||
"last_paths": {
|
||||
"gencad": "",
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"specctra_dsn": "",
|
||||
"step": "",
|
||||
"vrml": ""
|
||||
},
|
||||
"page_layout_descr_file": ""
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"drawing": {
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
"intersheets_ref_show": false,
|
||||
"intersheets_ref_suffix": "",
|
||||
"junction_size_choice": 3,
|
||||
"label_size_ratio": 0.375,
|
||||
"pin_symbol_size": 25.0,
|
||||
"text_offset_ratio": 0.15
|
||||
},
|
||||
"legacy_lib_dir": "",
|
||||
"legacy_lib_list": [],
|
||||
"meta": {
|
||||
"version": 1
|
||||
},
|
||||
"net_format_name": "",
|
||||
"ngspice": {
|
||||
"fix_include_paths": true,
|
||||
"fix_passive_vals": false,
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"model_mode": 0,
|
||||
"workbook_filename": ""
|
||||
},
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "",
|
||||
"spice_adjust_passive_values": false,
|
||||
"spice_external_command": "spice \"%I\"",
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
"3a05ed4e-145e-4c3c-a642-c544ff58e1e4",
|
||||
""
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
}
|
4267
hardware/sensor_temperature/sensor_temperature.kicad_sch
Normal file
4267
hardware/sensor_temperature/sensor_temperature.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
25
software/test_software/README.md
Normal file
25
software/test_software/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Python ModBus test program
|
||||
|
||||
A simple test program to test ModBus peripherals connected to a serial port.
|
||||
|
||||
## Configuration
|
||||
|
||||
Edit config.yaml.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python3
|
||||
- minimalmodbus
|
||||
- json
|
||||
- time
|
||||
- sys
|
||||
- logging
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2025 M.T. Konstapel
|
||||
|
||||
This program 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.
|
BIN
software/test_software/__pycache__/config_reader.cpython-36.pyc
Normal file
BIN
software/test_software/__pycache__/config_reader.cpython-36.pyc
Normal file
Binary file not shown.
BIN
software/test_software/__pycache__/modbus_control.cpython-36.pyc
Normal file
BIN
software/test_software/__pycache__/modbus_control.cpython-36.pyc
Normal file
Binary file not shown.
18
software/test_software/config.yml
Normal file
18
software/test_software/config.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
# This is the configuration file for the Mees Electronics weather station MK2
|
||||
|
||||
|
||||
# Global settings
|
||||
global:
|
||||
log-rf: /home/marcel/ham/weather_station/pe1rxf_aprs-rf.log # Log RF traffic to file (0=no logging)
|
||||
|
||||
# ModBus hardware settings
|
||||
modbus:
|
||||
#port: /dev/serial/by-path/platform-3f980000.usb-usb-0:1.3:1.0-port0 # USB port to which RS-485 dongle is connected
|
||||
port: /dev/ttyUSB0
|
||||
|
||||
# Modbus servers settings
|
||||
modbus_servers:
|
||||
- address: 14 # ModBus address of weather station
|
||||
description: Dual temperature sensor
|
||||
- address: 1
|
||||
description: Dummy
|
94
software/test_software/config_reader.py
Normal file
94
software/test_software/config_reader.py
Normal file
@@ -0,0 +1,94 @@
|
||||
'''
|
||||
# A basic APRS iGate and APRS weather station with additional (optional) PE1RXF telemetry support
|
||||
#
|
||||
# This program reads the registers of the PE1RXF weather station via ModBus RTU and sends it as
|
||||
# an APRS WX report over APRS. Additionally, it sends beacons and forwards received APRS messages
|
||||
# to the APRS-IS network. All configurable via a YAML file called pe1rxf_aprs.yml.
|
||||
#
|
||||
# This program also has a PE1RXF APRS telemetry to MQTT bridge, which is configurable via pe1rxf_telemetry.yml
|
||||
#
|
||||
# Copyright (C) 2023, 2024 M.T. Konstapel https://meezenest.nl/mees
|
||||
#
|
||||
# This file is part of weather_station
|
||||
#
|
||||
# weather_station 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.
|
||||
#
|
||||
# weather_station 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 weather_station. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import yaml
|
||||
from yaml.loader import SafeLoader
|
||||
|
||||
class config_reader:
|
||||
|
||||
# initiate class: define name configuration files
|
||||
def __init__(self, config_file):
|
||||
self.config_file = config_file
|
||||
|
||||
def read_settings(self):
|
||||
|
||||
if self.read_config_file() == 0:
|
||||
return 0
|
||||
|
||||
if self.test_global_settings() == 0:
|
||||
return 0
|
||||
|
||||
if self.test_modbus_settings() == 0:
|
||||
return 0
|
||||
|
||||
if self.test_modbus_servers_settings() == 0:
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
def read_config_file (self):
|
||||
try:
|
||||
with open(self.config_file) as f:
|
||||
self.config_file_settings = yaml.load(f, Loader=SafeLoader)
|
||||
except:
|
||||
print ("Configuration file ./" + self.config_file + " not found or syntax error in file.")
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
# Test if all settings are pressebt
|
||||
def test_global_settings(self):
|
||||
# Test is all expected settings are present
|
||||
try:
|
||||
tmp = self.config_file_settings['global']['log-rf']
|
||||
except:
|
||||
print ("Error in the global section of the configuration file.")
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
# Test if all settings are pressebt
|
||||
def test_modbus_settings(self):
|
||||
# Test is all expected settings are present
|
||||
try:
|
||||
tmp = self.config_file_settings['modbus']['port']
|
||||
except:
|
||||
print ("Error in the modbus section of the configuration file.")
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
def test_modbus_servers_settings(self):
|
||||
for entry in self.config_file_settings['modbus_servers']:
|
||||
try:
|
||||
tmp = entry['address']
|
||||
tmp = entry['description']
|
||||
except:
|
||||
print ("Error in the modbus_servers section of the configuration file.")
|
||||
return 0
|
||||
else:
|
||||
return 1
|
56
software/test_software/modbus_control.py
Normal file
56
software/test_software/modbus_control.py
Normal file
@@ -0,0 +1,56 @@
|
||||
""""
|
||||
ModBus control routines
|
||||
Copyright (C) 2023 M.T. Konstapel
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import minimalmodbus
|
||||
import serial
|
||||
|
||||
class ModBusController(minimalmodbus.Instrument):
|
||||
"""Instrument class for Epever Charge Controllers.
|
||||
|
||||
Args:
|
||||
* portname (str): port name
|
||||
* slaveaddress (int): slave address in the range 1 to 247
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, portname, slaveaddress):
|
||||
minimalmodbus.Instrument.__init__(self, portname, slaveaddress)
|
||||
self.serial.baudrate = 9600
|
||||
self.serial.bytesize = 8
|
||||
self.serial.parity = serial.PARITY_NONE
|
||||
self.serial.stopbits = 1
|
||||
self.serial.timeout = 0.3
|
||||
self.mode = minimalmodbus.MODE_RTU
|
||||
self.clear_buffers_before_each_transaction = True
|
||||
self.close_port_after_each_call = False
|
||||
|
||||
#Address range 0x3000
|
||||
def get_id(self):
|
||||
device_id = [None]*4
|
||||
device_id[0] = self.read_register(0, 0, 4, False)
|
||||
device_id[1] = self.read_register(1, 0, 4, False)
|
||||
device_id[2] = self.read_register(2, 0, 4, False)
|
||||
device_id[3] = self.read_register(3, 0, 4, False)
|
||||
|
||||
return device_id
|
||||
|
||||
def enable_heater(self):
|
||||
self.write_bit(0, 1, 5)
|
||||
|
||||
def disable_heater(self):
|
||||
self.write_bit(0, 0, 5)
|
121
software/test_software/weather_station_rs485_client.py
Normal file
121
software/test_software/weather_station_rs485_client.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# Copyright 2025 Marcel Konstapel
|
||||
# https://www.meezenest.nl/mees
|
||||
#
|
||||
# Testprogram for weather_station_MK2 sensors
|
||||
#
|
||||
# V0.1 2025-07-22
|
||||
# - First working program
|
||||
# V0.1.1 2025-07-28
|
||||
# - Added auto detect, reconnect and other fail safe features
|
||||
#
|
||||
# This program 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.
|
||||
#
|
||||
# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import json
|
||||
import time
|
||||
import sys
|
||||
import logging
|
||||
import minimalmodbus
|
||||
|
||||
from modbus_control import ModBusController
|
||||
from config_reader import config_reader
|
||||
|
||||
def setup():
|
||||
config_file = "config.yml"
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format="{asctime} - {levelname} - {message}",
|
||||
style="{",
|
||||
datefmt="%Y-%m-%d %H:%M",
|
||||
)
|
||||
|
||||
# Read the configuration file
|
||||
configuration = config_reader(config_file)
|
||||
|
||||
if configuration.read_settings() == 0:
|
||||
sys.exit()
|
||||
|
||||
# show values from config file
|
||||
logging.info("Using RS485 port : " + configuration.config_file_settings['modbus']['port'])
|
||||
|
||||
controller = []
|
||||
modbus_addresses = []
|
||||
for index, entry in enumerate(configuration.config_file_settings['modbus_servers']):
|
||||
modbus_addresses.append(entry['address'])
|
||||
logging.info("Using device: " + entry['description'] + " on ModBus address: " + str(entry['address']))
|
||||
|
||||
# Open serial port and try to connect to all the ModBus devices from the configuration file
|
||||
try:
|
||||
#controller = ModBusController(configuration.config_file_settings['modbus']['port'], modbus_addresses[0])
|
||||
controller.append(ModBusController(configuration.config_file_settings['modbus']['port'], modbus_addresses[index]))
|
||||
except:
|
||||
logging.error("Could not open serial port " + configuration.config_file_settings['modbus']['port'])
|
||||
sys.exit("Exiting")
|
||||
|
||||
return configuration, controller, modbus_addresses
|
||||
|
||||
# Lost serial port, try to reconnect. We can try to connect to any ModBus address, even if it does not exists. We are only trying to reconnect to the RS485 serial/USB dongle.
|
||||
def ReconnectModBus(configuration):
|
||||
Retrying = True
|
||||
while (Retrying):
|
||||
|
||||
logging.warning("Try to reconnect to ModBus dongle")
|
||||
try:
|
||||
controller = ModBusController(configuration.config_file_settings['modbus']['port'], 0)
|
||||
Retrying = False
|
||||
except:
|
||||
logging.error("Serial port " + configuration.config_file_settings['modbus']['port'] + " not available. Retry in 10 seconds.")
|
||||
time.sleep(10)
|
||||
|
||||
logging.warning("Reconnected to ModBus dongle.")
|
||||
|
||||
#print("Enable heater function")
|
||||
#controller.enable_heater()
|
||||
|
||||
Configuration, Controller, ModbusAddresses = setup()
|
||||
|
||||
while (1):
|
||||
time.sleep(3) # Sleep for 3 seconds
|
||||
ModBusData={}
|
||||
|
||||
# Loop through all configured ModBus devices
|
||||
for index, current_modbus_device in enumerate(ModbusAddresses):
|
||||
logging.info("Reading device on ModBus address: " + str(current_modbus_device))
|
||||
|
||||
try:
|
||||
ModBusData['ID'] = Controller[index].get_id()
|
||||
if ModBusData['ID'][0] == 0x4D45:
|
||||
logging.info("Found Mees Electronics sensor on ModBus address " + str(current_modbus_device))
|
||||
logging.info("Serial number: " + hex(ModBusData['ID'][1]) + " " + hex(ModBusData['ID'][2]) + " " + hex(ModBusData['ID'][3]))
|
||||
logging.info (json.dumps(ModBusData, indent=1, sort_keys=False))
|
||||
except minimalmodbus.NoResponseError:
|
||||
# Handle communication timeout
|
||||
logging.error("No response from the instrument")
|
||||
except minimalmodbus.InvalidResponseError:
|
||||
# Handle invalid Modbus responses
|
||||
logging.error("Invalid response from the instrument")
|
||||
except minimalmodbus.SlaveReportedException as e:
|
||||
# Handle errors reported by the slave device
|
||||
logging.error(f"The instrument reported an error: {e}")
|
||||
except minimalmodbus.ModbusException as e:
|
||||
# Handle all other Modbus-related errors
|
||||
logging.error(f"Modbus error: {e}")
|
||||
except Exception as e:
|
||||
# Handle unexpected errors, probably I/O error in USB/serial
|
||||
logging.error(f"Unexpected error: {e}" + ", serial port " + Configuration.config_file_settings['modbus']['port'] + " not available while reading device on ModBus address " + str(ModbusAddresses[index]) + ". Try to reconnect.")
|
||||
Controller[index].serial.close()
|
||||
ReconnectModBus(Configuration)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user