Форум сайта python.su
Добрый день всем)
Есть газоанализатор СТГ 3 с rs485 я пытаюсь считать с него текущие измеренное значение концентрации , а так же установленные пороги 1 и 2 .
Пример запроса данных:
01; 03; 00; 00; 00; 02; sum 0; sum 1,
где N номер сигнализатора в сети. Устанавливается пользователем в
диапазоне от 1 до 31;
sum 0; sum 1 контрольная сумма (CRC), рассчитывается в соответствии
с протоколом «MODBUS-RTU».
Вот мой код
from pymodbus.client.sync import ModbusSerialClient as ModbusClient import time # Параметры подключения port = 'COM6' baudrate = 9600 client = ModbusClient( method='rtu', port=port, baudrate=baudrate, stopbits=1, parity='N', timeout=1 ) def read_modbus_data(client, unit_id, address, count): # Подключение к клиенту if not client.connect(): print("Не удалось подключиться к Modbus серверу") return None # Чтение данных result = client.read_holding_registers(address, count, unit=unit_id) if result.isError(): print("Ошибка чтения данных: ", result) return None # Закрытие соединения client.close() return result def interpret_data(registers): if len(registers) < 2: print("Недостаточно данных для интерпретации") return # Извлечение данных из регистров reg0 = registers[0] reg1 = registers[1] # Разбиение регистров на байты byte1 = (reg0 >> 8) & 0xFF byte2 = reg0 & 0xFF byte3 = (reg1 >> 8) & 0xFF byte4 = reg1 & 0xFF # Извлечение состояния сигнализации и знака sign_bit = (byte1 >> 7) & 0x01 comma_position = (byte1 & 0x07) # Определение знака sign = '-' if sign_bit else '+' # Чтение значений integer_part = byte2 * 10000 + byte3 * 10 + (byte4 >> 4) decimal_part = (byte4 & 0x0F) * 100 # Формирование значения value = integer_part + decimal_part / 10000 if sign_bit: value = -value # Сдвиг запятой value /= 10 ** comma_position # Форматирование результата с округлением до 2 знаков после запятой value = round(value, 2) print(f"Значение: {value:.2f}") return value def main(): # Параметры запроса unit_id = 1 address = 0x00 # Начальный адрес для измеренного значения count = 2 # Количество регистров для измеренного значения threshold1_address = 0x02 # Адрес порога 1 threshold2_address = 0x04 # Адрес порога 2 while True: # Чтение данных измеренного значения data = read_modbus_data(client, unit_id, address, count) if data: print("Текущее значение концентрации:") interpret_data(data.registers) # Чтение порога 1 p1_data = read_modbus_data(client, unit_id, threshold1_address, count) if p1_data: print("Установленное значение порога 1:") interpret_data(p1_data.registers) # Чтение порога 2 p2_data = read_modbus_data(client, unit_id, threshold2_address, count) if p2_data: print("Установленное значение порога 2:") interpret_data(p2_data.registers) # Задержка 1 секунда time.sleep(1) if __name__ == "__main__": main()
Отредактировано atomo2 (Сен. 10, 2024 06:48:35)
Прикреплённый файлы:
1.png (91,7 KБ)
Офлайн
atomo2Посмотрите что в результате, вручную посчитайте что в байтах
result = client.read_holding_registers(address, count, unit=unit_id)
Офлайн
> у меня установлен первый порог 20 , а второй 40 , а код выдает первый 32 , а второй 64.
>>> 0x20 32 >>> 0x40 64
Офлайн
RodegastЭто то да)) это одинаковые значения, в шестнадцатеричной и десятичной системе. Но у меня то выводится в десятичной системе, значит считывается в 16ричной что то другое?
> у меня установлен первый порог 20 , а второй 40 , а код выдает первый 32 , а второй 64.
from pymodbus.client.sync import ModbusSerialClient as ModbusClient import time # Параметры подключения port = 'COM6' baudrate = 9600 client = ModbusClient( method='rtu', port=port, baudrate=baudrate, stopbits=1, parity='N', timeout=1 ) def read_modbus_data(client, unit_id, address, count): # Подключение к клиенту if not client.connect(): print("Не удалось подключиться к Modbus серверу") return None # Чтение данных result = client.read_holding_registers(address, count, unit=unit_id) if result.isError(): print("Ошибка чтения данных: ", result) return None # Закрытие соединения client.close() return result def interpret_data(registers, is_threshold=False): if len(registers) < 2: print("Недостаточно данных для интерпретации") return # Инициализация переменной value value = None # Извлечение данных из регистров reg0 = registers[0] reg1 = registers[1] if is_threshold: # Для порога младший и старший байты используются напрямую byte1 = reg0 & 0xFF byte2 = (reg0 >> 8) & 0xFF byte3 = reg1 & 0xFF byte4 = (reg1 >> 8) & 0xFF # Интерпретация данных hex_value = f"{reg0:04X} {reg1:04X}" print(f"Значение порога (16-ричное): {hex_value}") else: # Разбиение регистров на байты byte1 = (reg0 >> 8) & 0xFF byte2 = reg0 & 0xFF byte3 = (reg1 >> 8) & 0xFF byte4 = reg1 & 0xFF # Извлечение состояния сигнализации и знака sign_bit = (byte1 >> 7) & 0x01 comma_position = (byte1 & 0x07) # Определение знака sign = '-' if sign_bit else '+' # Чтение значений integer_part = byte2 * 10000 + byte3 * 10 + (byte4 >> 4) decimal_part = (byte4 & 0x0F) * 100 # Формирование значения value = integer_part + decimal_part / 10000 if sign_bit: value = -value # Сдвиг запятой value /= 10 ** comma_position # Форматирование результата с округлением до 2 знаков после запятой value = round(value, 2) print(f"Значение концентрации: {value:.2f}") return value def main(): # Параметры запроса unit_id = 1 address = 0x00 # Начальный адрес для измеренного значения count = 2 # Количество регистров для измеренного значения threshold1_address = 0x02 # Адрес порога 1 threshold2_address = 0x04 # Адрес порога 2 while True: # Чтение данных измеренного значения data = read_modbus_data(client, unit_id, address, count) if data: print("Текущее значение концентрации:") interpret_data(data.registers) # Чтение порога 1 p1_data = read_modbus_data(client, unit_id, threshold1_address, count) if p1_data: print("Установленное значение порога 1:") interpret_data(p1_data.registers, is_threshold=True) # Чтение порога 2 p2_data = read_modbus_data(client, unit_id, threshold2_address, count) if p2_data: print("Установленное значение порога 2:") interpret_data(p2_data.registers, is_threshold=True) # Задержка 1 секунда time.sleep(1) if __name__ == "__main__": main()
Отредактировано atomo2 (Сен. 11, 2024 06:31:48)
Офлайн
atomo2В инструкции написано, где ААА, это количество цифр после запятой, т. е 0587 0000, первая 5 обозначает, сколько цифр отсчитать справа налево, в другом числе также
но как теперь сделать из значения 0587 0000 число 8.7 , а из значения 0414 5000 число 14.5
Офлайн
Спасибо, получилось, чуть позже напишу код полностью
Вот рабочий код, с костылями конечно, но работает как нужно
from pymodbus.client.sync import ModbusSerialClient as ModbusClient import time # Параметры подключения port = 'COM6' baudrate = 9600 client = ModbusClient( method='rtu', port=port, baudrate=baudrate, stopbits=1, parity='N', timeout=1 ) def read_modbus_data(client, unit_id, address, count): # Подключение к клиенту if not client.connect(): print("Не удалось подключиться к Modbus серверу") return None # Чтение данных result = client.read_holding_registers(address, count, unit=unit_id) if result.isError(): print("Ошибка чтения данных: ", result) return None # Закрытие соединения client.close() return result def format_value_with_comma(hex_value, comma_position): # Преобразование 16-ричного значения в строку value_str = f"{hex_value:08X}" # Вставка запятой на нужную позицию integer_part = value_str[2:-comma_position] if comma_position < len(value_str) else '0' decimal_part = value_str[-comma_position:] if comma_position > 0 else '0' # Форматирование с двумя знаками после запятой formatted_value = f"{integer_part},{decimal_part}".lstrip('0') or '0' # Обрезаем до двух знаков после запятой if len(decimal_part) > 2: formatted_value = f"{integer_part},{decimal_part[:2]}" return formatted_value def display_registers(registers): if len(registers) < 2: print("Недостаточно данных для отображения") return # Извлечение значений регистров reg0 = registers[0] # Старший регистр reg1 = registers[1] # Младший регистр # Объединение регистров в одно 16-ричное число combined_hex = (reg0 << 16) | reg1 # Позиция запятой из старшего байта первого регистра (используем 8 младших бит) comma_position = (reg0 >> 8) & 0xFF # Форматирование значения с запятой formatted_value = format_value_with_comma(combined_hex, comma_position) # Вывод только отформатированного значения print(f"Значение: {formatted_value}") def display_concentration(registers): if len(registers) < 2: print("Недостаточно данных для отображения концентрации") return # Извлечение значений регистров reg0 = registers[0] # Старший регистр reg1 = registers[1] # Младший регистр # Разбиение регистров на байты byte1 = (reg0 >> 8) & 0xFF byte2 = reg0 & 0xFF byte3 = (reg1 >> 8) & 0xFF byte4 = reg1 & 0xFF # Извлечение состояния сигнализации и знака sign_bit = (byte1 >> 7) & 0x01 comma_position = (byte1 & 0x07) # Определение знака sign = '-' if sign_bit else '+' # Чтение значений integer_part = byte2 * 10000 + byte3 * 10 + (byte4 >> 4) decimal_part = (byte4 & 0x0F) * 100 # Формирование значения value = integer_part + decimal_part / 10000 if sign_bit: value = -value # Сдвиг запятой value /= 10 ** comma_position # Форматирование результата с округлением до 2 знаков после запятой value = round(value, 2) print(f"Значение концентрации: {value:.2f}") def main(): # Параметры запроса unit_id = 1 count = 2 # Количество регистров для порога и концентрации threshold1_address = 0x02 # Адрес порога 1 threshold2_address = 0x04 # Адрес порога 2 concentration_address = 0x00 # Адрес регистров концентрации while True: # Чтение порога 1 p1_data = read_modbus_data(client, unit_id, threshold1_address, count) if p1_data: print("Установленное значение порога 1:") display_registers(p1_data.registers) # Чтение порога 2 p2_data = read_modbus_data(client, unit_id, threshold2_address, count) if p2_data: print("Установленное значение порога 2:") display_registers(p2_data.registers) # Чтение концентрации conc_data = read_modbus_data(client, unit_id, concentration_address, count) if conc_data: print("Измеренное значение концентрации:") display_concentration(conc_data.registers) # Задержка 1 секунда time.sleep(1) if __name__ == "__main__": main()
Отредактировано atomo2 (Сен. 11, 2024 12:17:24)
Офлайн
Теперь же мне осталось реализовать последнюю штуку , а именно запись порогов и пгсов
Вот код, но до 15 включительно записывается правильное значение , а если я ввожу 20 , то записывается 14, если 26 то 20 Ткните носом где у меня преобразование не правильно происходит?
import tkinter as tk import serial # Функция для вычисления CRC16 def calculate_crc(data): crc = 0xFFFF for byte in data: crc ^= byte for _ in range(8): if crc & 0x0001: crc = (crc >> 1) ^ 0xA001 else: crc >>= 1 return crc.to_bytes(2, byteorder='little') # Функция для записи значения в Modbus def write_modbus(set1, value): # Преобразование значения в формат BCD bh = bl = bih = 0x00 bil = value & 0xFF # Значение в `BIL` (младший байт) # Формирование команды command = bytearray([ 0x01, # Адрес устройства 0x10, # Код функции (Запись в несколько регистров) 0x00, 0x20, # Начальный адрес (0x0020) 0x00, 0x03, # Количество регистров (3) 0x06, # Количество байтов данных (6) set1, # Уставка (1 байт) 0x00, # Нулевой байт bh, bl, bih, bil # BH, BL, BIH, BIL ]) # Добавление CRC crc = calculate_crc(command) command.extend(crc) # Отладочная информация print("Команда для отправки:", command.hex().upper()) # Отправка команды по COM-порту with serial.Serial('COM6', 9600, timeout=1) as ser: ser.write(command) response = ser.read(8) # Считывание ответа (если он есть) print("Ответ:", response.hex().upper()) # Обработчик для кнопки установки порога 1 def on_set_threshold1(): try: # Чтение значения из текстового поля и преобразование в целое число value = int(entry_threshold1.get(), 10) if value < 0 or value > 255: raise ValueError("Значение должно быть от 0 до 255") # Отправка команды write_modbus(0x10, value) # Уставка 0x10 и значение в `BIL` except ValueError as e: print("Ошибка:", e) # Создание интерфейса root = tk.Tk() root.title("Modbus Interface") # Окно для установки первого порога tk.Label(root, text="Установка порога 1").pack(pady=5) entry_threshold1 = tk.Entry(root) entry_threshold1.pack(pady=5) entry_threshold1.insert(0, '0') tk.Button(root, text="Установить порог 1", command=on_set_threshold1).pack(pady=5) root.mainloop()
Отредактировано atomo2 (Сен. 11, 2024 12:34:45)
Офлайн
> Но у меня то выводится в десятичной системе, значит считывается в 16ричной что то другое?
Это значит что ты переводишь шестнадцатеричное число в десятичное.
> как теперь сделать из значения 0587 0000 число 8.7 , а из значения 0414 5000 число 14.5
Подсказываю:
05 870000 - 5 цифр после запятой = 8,70000
04 145000 - 4 цифры после запятой = 14,5000
Отредактировано Rodegast (Сен. 11, 2024 12:51:33)
Офлайн
RodegastСпасибо))
Подсказываю:
05 870000 - 5 цифр после запятой = 8,70000
04 145000 - 4 цифры после запятой = 14,5000
Офлайн
> сейчас проблема в последнем моем посте
def write_modbus(set1, value): # Преобразование значения в формат BCD bh = bl = bih = 0x00 bil = value & 0xFF # Значение в `BIL` (младший байт)
bh = 0b00000100 # знак +, пороги не сработали, разрядов 4 bl = 0b00011111 # 31 bih = 0b00101001 # 41 bil = 0b00110010 # 50
Отредактировано Rodegast (Сен. 11, 2024 19:24:51)
Офлайн