Уведомления

Группа в Telegram: @pythonsu

#1 Сен. 10, 2024 06:28:25

atomo2
Зарегистрирован: 2024-09-10
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Считывание данных по modbus

Добрый день всем)
Есть газоанализатор СТГ 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()



А получаю какую то ерунду с порогами, сейчас у меня установлен первый порог 20 , а второй 40 , а код выдает первый 32 , а второй 64. Так же текущее значение тоже не совсем точное , есть какая то погрешность. Если в официальной программе такие значения



А мой код выдает


Текущее значение концентрации: -0.34
Установленное значение порога 1: 32.00
Установленное значение порога 2: 64.00

Текущее значение концентрации: -0.33
Установленное значение порога 1: 32.00
Установленное значение порога 2: 64.00

Что я делаю не так? Спасибо.

Отредактировано atomo2 (Сен. 10, 2024 06:48:35)

Прикреплённый файлы:
attachment 1.png (91,7 KБ)

Офлайн

#2 Сен. 10, 2024 10:31:54

xam1816
Зарегистрирован: 2020-05-11
Сообщения: 1352
Репутация: +  118  -
Профиль   Отправить e-mail  

Считывание данных по modbus

atomo2
result = client.read_holding_registers(address, count, unit=unit_id)
Посмотрите что в результате, вручную посчитайте что в байтах

Офлайн

#3 Сен. 10, 2024 14:59:41

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2750
Репутация: +  184  -
Профиль   Отправить e-mail  

Считывание данных по modbus

> у меня установлен первый порог 20 , а второй 40 , а код выдает первый 32 , а второй 64.

 >>> 0x20
32
>>> 0x40
64



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Офлайн

#4 Сен. 11, 2024 04:03:00

atomo2
Зарегистрирован: 2024-09-10
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Считывание данных по modbus

Rodegast
> у меня установлен первый порог 20 , а второй 40 , а код выдает первый 32 , а второй 64.
Это то да)) это одинаковые значения, в шестнадцатеричной и десятичной системе. Но у меня то выводится в десятичной системе, значит считывается в 16ричной что то другое?

Изменил код считываю в 16 ричной системе

 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()


И получаю

Текущее значение концентрации:
Значение концентрации: 0.09
Установленное значение порога 1:
Значение порога (16-ричное): 0587 0000
Установленное значение порога 2:
Значение порога (16-ричное): 0414 5000

Все верно , но как теперь сделать из значения 0587 0000 число 8.7 , а из значения 0414 5000 число 14.5 . Понятно, что тут это младший байт первого регистра число 8 , но и в нем же 7. Во втором пороге младший байт 14 первого регистра и старший второго регистра 50

Отредактировано atomo2 (Сен. 11, 2024 06:31:48)

Офлайн

#5 Сен. 11, 2024 07:53:29

xam1816
Зарегистрирован: 2020-05-11
Сообщения: 1352
Репутация: +  118  -
Профиль   Отправить e-mail  

Считывание данных по modbus

atomo2
но как теперь сделать из значения 0587 0000 число 8.7 , а из значения 0414 5000 число 14.5
В инструкции написано, где ААА, это количество цифр после запятой, т. е 0587 0000, первая 5 обозначает, сколько цифр отсчитать справа налево, в другом числе также

Офлайн

#6 Сен. 11, 2024 09:54:51

atomo2
Зарегистрирован: 2024-09-10
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Считывание данных по modbus

Спасибо, получилось, чуть позже напишу код полностью

Вот рабочий код, с костылями конечно, но работает как нужно

 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)

Офлайн

#7 Сен. 11, 2024 12:22:55

atomo2
Зарегистрирован: 2024-09-10
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Считывание данных по modbus

Теперь же мне осталось реализовать последнюю штуку , а именно запись порогов и пгсов

Вот код, но до 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()


Мои мысли такие что от 20 мы должны писать уже не только в BIL а и в BH, BL, BIH

Отредактировано atomo2 (Сен. 11, 2024 12:34:45)

Офлайн

#8 Сен. 11, 2024 12:42:05

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2750
Репутация: +  184  -
Профиль   Отправить e-mail  

Считывание данных по modbus

> Но у меня то выводится в десятичной системе, значит считывается в 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)

Офлайн

#9 Сен. 11, 2024 14:47:53

atomo2
Зарегистрирован: 2024-09-10
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Считывание данных по modbus

Rodegast
Подсказываю:
05 870000 - 5 цифр после запятой = 8,70000
04 145000 - 4 цифры после запятой = 14,5000

Спасибо))
Так это я уже сделал, рабочий код выше опубликовал) сейчас проблема в последнем моем посте

Офлайн

#10 Сен. 11, 2024 18:52:54

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2750
Репутация: +  184  -
Профиль   Отправить e-mail  

Считывание данных по modbus

> сейчас проблема в последнем моем посте

 def write_modbus(set1, value):
    # Преобразование значения в формат BCD
    bh = bl = bih = 0x00
    bil = value & 0xFF  # Значение в `BIL` (младший байт)

У тебя та же самая ошибка. bh, bl, bih, bil это байты из таблицы А.2 А ты вместо того что бы их нормально сформировать какую то чухню записываешь. Например что бы отправить 3.1415 тебе нужно передать примерно такое:
 bh  = 0b00000100  # знак +, пороги не сработали, разрядов 4
bl   = 0b00011111  # 31
bih  = 0b00101001  # 41
bil  = 0b00110010  # 50



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Отредактировано Rodegast (Сен. 11, 2024 19:24:51)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version