Compare commits

...

2 Commits

Author SHA1 Message Date
marcel
f5a6937786 ModBus software works, MQTT bridge software added. 2025-08-14 11:47:32 +02:00
marcel
9e21f6a011 ModBus software works, MQTT bridge software added. 2025-08-14 11:45:47 +02:00
18 changed files with 591 additions and 102 deletions

View File

@@ -1,6 +1,6 @@
# Python ModBus test program
# Python MQTT to pe1rxf telemetry bridge
A simple test program to test ModBus peripherals connected to a serial port.
Forwards MQTT messages to AX.25 according to the PE1RXF telemetry protocol
## Configuration
@@ -15,6 +15,8 @@ Edit config.yaml.
- sys
- logging
AX.25 stack on Linux. "/usr/sbin/beacon" installed.
## License
Copyright (C) 2025 M.T. Konstapel

View File

@@ -0,0 +1,37 @@
# This is the configuration file for the Mees Electronics weather station MK2
# Global settings
global:
program-log: 0 # All program output will be written to this file (0 = do not log to file)
#program-log: /home/marcel/rs458.log # All program output will be written to this file (0 = do not log to file)
mqtt-server: mqtt.meezenest.nl
mqtt-port: 1883
# APRS settings
aprs:
- port: ax1 # Linux AX.25 port to which APRS weather report is sent
from_call: PE1RXF-13 # Call from which transmissions are made (can be a different call from the call assigned to the AX.25 port)
to_call: PE1RXF-1 # Call of the receiver
destination: APZMDM # APRS destination
digipath: 0 # Digipeater path for weather reports (0 = no path)
interval: 30
# - port: ax1 # Linux AX.25 port to which APRS weather report is sent
# call: PE1RXF-13 # Call from which transmissions are made (can be a different call from the call assigned to the AX.25 port)
# destination: APZMDM # APRS destination
# digipath: WIDE2-1 # Digipeater path for weather reports (0 = no path)
# interval: 30
# Define the MQTT data to transmit over AX25. The order of the items is the order the data is combined to the telemetry string.
mqtt:
subscribe:
- mees_electronics/4d45000000000002/wind_direction
- mees_electronics/4d45000000000002/wind_speed
- mees_electronics/4d45000000000002/wind_gust
- mees_electronics/4d45000000000002/rain_last_hour
- mees_electronics/4d45000000000002/rain_last_24 hours
- mees_electronics/4d45000000000002/temperature
- mees_electronics/4d45000000000002/humidity
- mees_electronics/4d45000000000002/barometric_pressure
- mees_electronics/4d45000000000002/temperature_pressure_sensor
- mees_electronics/4d45000000000002/status_bits
- mees_electronics/4d45000000000002/luminosity

View File

@@ -0,0 +1,90 @@
""""
ModBus configuration file reader routines
Copyright (C) 2023-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.
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 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_aprs_settings() == 0:
return 0
if self.test_mqtt_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 pressent
def test_global_settings(self):
# Test is all expected settings are present
try:
tmp = self.config_file_settings['global']['program-log']
tmp = self.config_file_settings['global']['mqtt-server']
tmp = self.config_file_settings['global']['mqtt-port']
except:
print ("Error in the global section of the configuration file.")
return 0
else:
return 1
def test_aprs_settings(self):
for entry in self.config_file_settings['aprs']:
try:
tmp = entry['port']
tmp = entry['from_call']
tmp = entry['to_call']
tmp = entry['destination']
tmp = entry['digipath']
tmp = entry['interval']
except:
print ("Error in the aprs section of the configuration file.")
return 0
else:
return 1
def test_mqtt_settings(self):
# Test is all expected settings are present
try:
tmp = self.config_file_settings['mqtt']['subscribe']
except:
print ("Error in the mqtt section of the configuration file.")
return 0
else:
return 1

View File

@@ -0,0 +1,10 @@
import logging
def on_message(client, userdata, message, properties=None):
logging.info(
f"Received message {message.payload} on topic '{message.topic}' with QoS {message.qos}"
)
def on_subscribe(client, userdata, mid, qos, properties=None):
logging.info(f"Subscribed with QoS {qos}")

View File

@@ -0,0 +1,215 @@
""""
MQTT to pe1rxf telemetry for Mees Electronics sensors.
Subscribes to MQTT broker entries, combines the values and sends
the data as pe1rxf telemetry over an AX.25 port.
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.
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 datetime
import subprocess
import os
import random
from pathlib import Path
from config_reader import config_reader
import paho.mqtt.client as mqtt
from paho.mqtt.client import CallbackAPIVersion
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
properties=Properties(PacketTypes.PUBLISH)
properties.MessageExpiryInterval=30 # in seconds
#import ssl
class MqttHandler:
def __init__(self, config):
self.config = config
# Define list with length equal to the number of subscriptions defined in the config file
self.number_of_mqtt_subscriptions = len(config['mqtt']['subscribe'])
self.aprs_telemetry_data = ['0.0']*self.number_of_mqtt_subscriptions
def process_message(self, client, userdata, message):
# Loop through all mqtt:subscribe: entries in config.yaml and see which mqtt message we have. Put in in the correct position of the APRS telemetry string
for index, entry in enumerate(self.config['mqtt']['subscribe']):
if entry == message.topic:
self.aprs_telemetry_data[index] = message.payload
logging.info(f"Received message {message.payload} on topic '{message.topic}' with QoS {message.qos}")
def on_connect(self, client, userdata, flags, reason_code, properties=None):
logging.info('Connected to MQTT broker.')
# Format the pe1rxf telemetry string
def get_aprs_telemetry_string(self):
data_string = []
for entry in self.aprs_telemetry_data:
data_string.append(entry.decode('utf-8'))
data_string = ','.join(data_string)
return data_string
def start_mqtt(config):
version = '3' # or '5'
mqtt_transport = 'tcp' # or 'websockets'
client_id = f'mees_electronics-mqtt-{random.randint(0, 1000)}'
if version == '5':
client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id=client_id,
transport=mqtt_transport,
protocol=mqtt.MQTTv5)
if version == '3':
client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id=client_id,
transport=mqtt_transport,
protocol=mqtt.MQTTv311,
clean_session=True)
#client.username_pw_set("user", "password")
mqtt_handler = MqttHandler(config)
client.on_connect = mqtt_handler.on_connect;
client.on_message = mqtt_handler.process_message
#client.on_message = mqtt_callbacks.on_message;
#client.on_publish = mqtt_callbacks.on_publish;
#client.on_subscribe = mqtt_callbacks.on_subscribe;
if version == '5':
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
properties=Properties(PacketTypes.CONNECT)
properties.SessionExpiryInterval=30*60 # in seconds
client.connect(config['global']['mqtt-server'],
port=config['global']['mqtt-port'],
clean_start=mqtt.MQTT_CLEAN_START_FIRST_ONLY,
properties=properties,
keepalive=60);
elif version == '3':
client.connect(config['global']['mqtt-server'], port=config['global']['mqtt-port'], keepalive=60);
client.loop_start();
return client, mqtt_handler
def setup():
config_file = "config.yaml"
# Read the configuration file
configuration = config_reader(config_file)
if configuration.read_settings() == 0:
sys.exit()
print("Succesfully read the configuration from file: " + config_file)
# show values from config file
if configuration.config_file_settings['global']['program-log'] == 0:
print("Program output will not be logged to a file.")
else:
print("Program output will be logged to file: " + configuration.config_file_settings['global']['program-log'])
# Enable the logging module. Log to file if enabled in configuration file, otherwise log to stand i/o (probably the screen)
logging.basicConfig(
level=logging.DEBUG,
format="{asctime} - {levelname} - {message}",
style="{",
datefmt="%Y-%m-%d %H:%M",
filename=configuration.config_file_settings['global']['program-log'],
)
logging.info("Connecting to MQTT broker: " + configuration.config_file_settings['global']['mqtt-server'])
# Start MQTT section
mqtt_connected = False
while not mqtt_connected:
try:
mqtt_client, mqtt_handler = start_mqtt(configuration.config_file_settings)
mqtt_connected = True
except:
logging.error("Could not connect to MQTT broker. Retry until success (of until CTRL-C is pressed).")
time.sleep(3) # Sleep for 3 seconds
subscribe_to_mqtt(configuration.config_file_settings, mqtt_client)
# End MQTT section
return configuration, mqtt_client, mqtt_handler
def subscribe_to_mqtt(configuration, client):
for index, entry in enumerate(configuration['mqtt']['subscribe']):
logging.debug("Subscribed to MQTT topic: " + str(entry) )
client.subscribe(topic=str(entry));
# It is not possible to send multiple beacons in a short time. Call this function not faster than every 3 seconds or so.
def send_data_to_aprs(weather_data, configuration):
to_call = configuration['to_call'].ljust(9)[:9]
# Define the Bash script and its arguments as a list
script = "/usr/sbin/beacon"
if configuration['digipath'] == 0:
arguments = ["-c", f"{configuration['from_call']}", "-d", f"{configuration['destination']}", "-s", f"{configuration['port']}", f":{to_call}:{weather_data}"]
else:
#arguments = ["-d", f"{configuration['destination']} {configuration['digipath']}", "-s", f"{configuration['port']}", f":{to_call}:{weather_data}"]
arguments = ["-c", f"{configuration['from_call']}", "-d", f"{configuration['destination']} {configuration['digipath']}", "-s", f"{configuration['port']}", f":{to_call}:{weather_data}"]
# Combine the script and its arguments into a single list
command = [script] + arguments
# Run the script
logging.debug(command)
try:
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Check the result
if result.returncode == 0:
logging.info("Send data to APRS radio.")
else:
logging.error("Failed to send data to APRS radio.")
logging.error(f"Reason: {result.stderr}")
except Exception as e:
logging.error("Failed to send data to APRS radio.")
logging.error(f"Command returned: {e}")
Configuration, MqttClient, mqtt_handler = setup()
LoopCounter = 0
while (1):
time.sleep(3) # Sleep for 3 seconds
# Send APRS telemetry every 10 cycles = every 10 minutes
LoopCounter = LoopCounter + 1
if LoopCounter >= 1:
# Send data to LoRa radio via external program (/usr/sbin/beacon). Make sure we use all radios defined in the configuration file.
for entry in Configuration.config_file_settings['aprs']:
send_data_to_aprs(mqtt_handler.get_aprs_telemetry_string(), entry)
logging.debug(mqtt_handler.get_aprs_telemetry_string())
# We cannot send multiple APRS messages in a short period of time, so we wait 3 deconds between messages.
time.sleep(3)
LoopCounter = 0

View File

@@ -0,0 +1,33 @@
# RS485 ModBus client to MQTT bridge
Scans the configured ModBus for Mees Electronics sensors and publishes the sensor data to the configured MQTT broker.
The Mees Electronics sensors are almost plug-and-play. You just have to set the sensor to a unique address and add this address to the config.yaml file. The description entry in the YAML file is only there for convenience.
## Configuration
Edit config.yaml.
The file modbus_registers.yaml contains the Mees Electronics register definitions of the various sensors. The newest definition file can be downloaded from the git repository.
## Requirements
- Python3
- minimalmodbus
- json
- time
- sys
- logging
- os
- pathlib
- paho.mqtt.client
- pyyaml
## License
Copyright (C) 2025 M.T. Konstapel (https://meezenest.nl/mees)
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.

View File

@@ -1,6 +1,5 @@
# This is the configuration file for the Mees Electronics weather station MK2
# Global settings
global:
program-log: 0 # All program output will be written to this file (0 = do not log to file)
@@ -21,15 +20,3 @@ modbus_servers:
description: Dual temperature sensor
- address: 1
description: Dummy
aprs:
- port: ax0 # Linux AX.25 port to which APRS weather report is sent
call: PE1RXF-13 # Call from which transmissions are made (can be a different call from the call assigned to the AX.25 port)
destination: APZMDM # APRS destination
digi_path: 0 # Digipeater path for weather reports (0 = no path)
interval: 30
- port: ax1 # Linux AX.25 port to which APRS weather report is sent
call: PE1RXF-13 # Call from which transmissions are made (can be a different call from the call assigned to the AX.25 port)
destination: APZMDM # APRS destination
digi_path: WIDE2-1 # Digipeater path for weather reports (0 = no path)
interval: 30

View File

@@ -38,9 +38,6 @@ class config_reader:
if self.test_modbus_servers_settings() == 0:
return 0
if self.aprs_settings() == 0:
return 0
return 1
def read_config_file (self):
@@ -89,17 +86,3 @@ class config_reader:
return 0
else:
return 1
def aprs_settings(self):
for entry in self.config_file_settings['aprs']:
try:
tmp = tmp = entry['port']
tmp = entry['call']
tmp = entry['destination']
tmp = entry['digi_path']
tmp = entry['interval']
except:
print ("Error in the aprs section of the configuration file.")
return 0
else:
return 1

View File

@@ -0,0 +1,107 @@
""""
ModBus control routines
Copyright (C) 2023-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.
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
self.watchdog_toggle_bit = 1
# Read the device ID
def get_id(self):
device_id = [None]*4
device_id[0] = self.read_register(1000, 0, 4, False)
device_id[1] = self.read_register(1001, 0, 4, False)
device_id[2] = self.read_register(1002, 0, 4, False)
device_id[3] = self.read_register(1003, 0, 4, False)
return device_id
# Read the device type
def get_type(self):
device_type = self.read_register(1004, 0, 4, False)
return device_type
# Get the 40 character long description string from ModBus device
def get_type_string(self):
register_offset = 1006 # ModBus register for text string starts at 1006
device_type_register = [None]*20 # ModBus register for text string is 20 integers long
device_type_string = "" # Store the decoded string here
for index, entry in enumerate(device_type_register):
# Read Type string registers
device_type_register[index] = self.read_register(index + register_offset, 0, 4, False)
# Convert to ASCII string
device_type_string += chr((device_type_register[index] & 0xFF00) >> 8)
device_type_string += chr(device_type_register[index] & 0xFF)
return device_type_string.strip()
# Ask the device how many input registers it has
def get_number_of_input_registers(self):
device_number_of_input_registers = self.read_register(1026, 0, 4, False)
return device_number_of_input_registers
# Read all the input registers
def read_all_input_registers(self):
number_of_input_registers = self.get_number_of_input_registers()
input_register = [None]*number_of_input_registers
for index in range(number_of_input_registers):
input_register[index] = self.read_register(index, 0, 4, False)
return input_register
def set_watchdog(self):
self.write_bit(0, 1, 5)
def reset_watchdog(self):
self.write_bit(0, 0, 5)
# Toggle the devices watchdog
def toggle_watchdog(self):
if self.watchdog_toggle_bit:
self.set_watchdog()
self.watchdog_toggle_bit = 0
else:
self.reset_watchdog()
self.watchdog_toggle_bit = 1

View File

@@ -0,0 +1,56 @@
""""
ModBus definition file reader routines
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.
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 yaml
from yaml.loader import SafeLoader
class definition_file_reader:
# initiate class: define name configuration files
def __init__(self, definition_file):
self.definition_file = definition_file
def read_settings(self):
if self.read_definition_file() == 0:
return 0
if self.test_definition_file() == 0:
return 0
return 1
def read_definition_file (self):
try:
with open(self.definition_file) as f:
self.definition_file_data = yaml.load(f, Loader=SafeLoader)
except:
print ("Definition file ./" + self.definition_file + " not found or syntax error in file.")
return 0
else:
return 1
# Test if all settings are pressent
def test_definition_file(self):
# Test is all expected settings are present
try:
tmp = self.definition_file_data['devices']
except:
print ("Error in the ModBus definition file.")
return 0
else:
return 1

View File

@@ -0,0 +1,30 @@
# This file defines the Mees Electronincs ModBus device registers
devices:
- device_type: 1
input_registers: 6 # The number of available input registers, starting from offset 40
input_register_names: # Description, unit
- [Temperature A, °C]
- [Temperature B , °C]
- [Minimum temperature A, °C]
- [Minimum temperature B, °C]
- [Maximum temperature A, °C]
- [Maximum temperature B, °C]
- device_type: 2
input_registers: 15 # The number of available input registers, starting from offset 40
input_register_names: # Description, unit, scaling 0 = as is, 1 = decimal one position to the left, 2 = decimal two positions to the left, enz.
- [weater_station_id, '', 0]
- [wind_direction, °, 1]
- [wind_speed, 'km/h', 2]
- [wind_gust, 'km/h', 2]
- [temperature, °C, 2]
- [rain_last_hour, 'l/m2', 2]
- [rain_last_24 hours, 'l/m2', 2]
- [rain_since_midnight, 'l/m2', 2]
- [humidity, '%', 2]
- [barometric_pressure, hPa, 1]
- [luminosity, 'W/m2', 0]
- [snow_fall, NA, 0]
- [raw_rainfall_counter, mm, 0]
- [temperature_pressure_sensor, °C, 2]
- [status_bits, '', 0]

View File

@@ -1,5 +1,7 @@
""""
ModBus test routines
ModBus client program. Scans the RS-485 ModBus for Mees Electronics
sensors and publishes the sensor data to an MQTT broker.
Copyright (C) 2025 M.T. Konstapel
This program is free software: you can redistribute it and/or modify
@@ -21,8 +23,8 @@ import sys
import logging
import minimalmodbus
import datetime
import subprocess
import os
import random
from pathlib import Path
from modbus_control import ModBusController
@@ -41,13 +43,14 @@ def start_mqtt(config):
version = '3' # or '5'
mqtt_transport = 'tcp' # or 'websockets'
client_id = f'mees_electronics-mqtt-{random.randint(0, 1000)}'
if version == '5':
client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id="myPy",
client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id=client_id,
transport=mqtt_transport,
protocol=mqtt.MQTTv5)
if version == '3':
client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id="myPy",
client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id=client_id,
transport=mqtt_transport,
protocol=mqtt.MQTTv311,
clean_session=True)
@@ -76,16 +79,11 @@ def start_mqtt(config):
client.loop_start();
# Debug code
# while 1:
# client.publish(mqtt_topic,'Cedalo Mosquitto is awesome',2,properties=properties);
# time.sleep(1)
return client
def setup():
config_file = "config.yml"
config_file = "config.yaml"
definition_file = "modbus_registers.yaml"
# Read the configuration file
@@ -177,35 +175,6 @@ def data_logger(data, configuration):
except:
logging.warning("Could not write to file: " + new_filename)
# It is not possible to send multiple beacons in a short time. Call this function not faster than every 3 seconds or so.
def send_data_to_aprs(weather_data, configuration):
# Define the Bash script and its arguments as a list
script = "/usr/sbin/beacon"
if configuration['digi_path'] == 0:
arguments = ["-d", f"{configuration['destination']}", "-s", "ax1", ":PE1RXF-3 :test2"]
else:
arguments = ["-d", f"{configuration['destination']} {configuration['digi_path']}", "-s", "ax1", ":PE1RXF-3 :test3"]
# Combine the script and its arguments into a single list
command = [script] + arguments
# Run the script
logging.debug(command)
try:
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Check the result
if result.returncode == 0:
logging.info("Send data to APRS radio.")
else:
logging.error("Failed to send data to APRS radio.")
logging.error(f"Reason: {result.stderr}")
except Exception as e:
logging.error("Failed to send data to APRS radio.")
logging.error(f"Command returned: {e}")
def send_data_to_mqtt(data, configuration, modbus_registers, mqtt_client):
mqtt_top_topic = []
@@ -271,19 +240,6 @@ LoopCounter = 0
while (1):
time.sleep(3) # Sleep for 3 seconds
# Send APRS telemetry every 10 cycles = every 10 minutes
'''
LoopCounter = LoopCounter + 1
if LoopCounter >= 1:
# Send data to LoRa radio via external program (/usr/sbin/beacon). Make sure we use all radios defined in the configuration file.
for entry in Configuration.config_file_settings['aprs']:
send_data_to_aprs(1, entry)
# We cannot send multiple APRS messages in a short period of time, so we wait 3 deconds between messages.
time.sleep(3)
LoopCounter = 0
'''
ModBusData={}
# Loop through all configured ModBus devices and try to read the sensor data
@@ -299,7 +255,7 @@ while (1):
ModBusData['InputRegisters'] = Controller[index].read_all_input_registers()
# Keep the watchdog from resetting the ModBusdevice
#Controller[index].toggle_watchdog()
Controller[index].toggle_watchdog()
NoError = True
except minimalmodbus.NoResponseError:

View File

@@ -1,15 +0,0 @@
2025-08-01 16:23 - INFO - Succesfully read the configuration from file: config.yml
2025-08-01 16:23 - INFO - Program output will be logged to file: /home/marcel/rs458.log
2025-08-01 16:23 - INFO - Sensor data will be logged to file: /home/marcel/weather_station_data.log
2025-08-01 16:23 - INFO - Using RS485 port : /dev/ttyUSB0
2025-08-01 16:23 - INFO - Succesfully read the ModBus register definitions from file: modbus_registers.yaml
2025-08-01 16:23 - INFO - Using device: Dual temperature sensor on ModBus address: 14
2025-08-01 16:23 - INFO - Using device: Dummy on ModBus address: 1
2025-08-01 16:23 - WARNING - No response from the instrument on ModBus address: 1
2025-08-01 16:24 - INFO - Succesfully read the configuration from file: config.yml
2025-08-01 16:24 - INFO - Program output will be logged to file: /home/marcel/rs458.log
2025-08-01 16:24 - INFO - Sensor data will be logged to file: /home/marcel/weather_station_data.log
2025-08-01 16:24 - INFO - Using RS485 port : /dev/ttyUSB0
2025-08-01 16:24 - INFO - Succesfully read the ModBus register definitions from file: modbus_registers.yaml
2025-08-01 16:24 - INFO - Using device: Dual temperature sensor on ModBus address: 14
2025-08-01 16:24 - INFO - Using device: Dummy on ModBus address: 1

View File

@@ -1,2 +0,0 @@
{"DateTime": "2025-08-01T16:55:39", "ID": [19781, 0, 0, 1], "Type": 1, "TypeString": "Dual temperature sensor", "InputRegisters": [20, 21, 22, 24, 23, 25]}
{"DateTime": "2025-08-01T16:55:43", "ID": [19781, 0, 0, 1], "Type": 1, "TypeString": "Dual temperature sensor", "InputRegisters": [20, 21, 22, 24, 23, 25]}