Страница 2 из 2 ПерваяПервая 12
Показано с 11 по 16 из 16

Тема: СТГ 3 и modbus

  1. #11
    Пользователь
    Регистрация
    27.11.2011
    Адрес
    Краснодар
    Сообщений
    13,316

    По умолчанию

    у вас bcd это когда тетрада байта не может быть больше 9? то есть в байте никогда не бывает букв от А до F ?

    Код:
            /// <summary>
            /// Конвертирование десятичного числа в формат BCD (Используется в MBus, приборах Пульсар)
            /// </summary>
            /// <param name="dec"></param>
            /// <returns></returns>
            public static int DecToBCD(int dec)
            {
                int num = 0;
                int num1 = 0;
                while (dec != 0)
                {
                    num = num | dec % 10 << (num1 & 31);
                    num1 += 4;
                    dec /= 10;
                }
                return num;
            }
    
            /// <summary>
            /// Конвертирование числа в формате BCD в десятичный формат (Используется в MBus, приборах Пульсар)
            /// </summary>
            /// <param name="bcdNumber"></param>
            /// <returns></returns>
    
            public static long BcdToDec(byte[] bcdNumber)
            {
                long result = 0;
                foreach (byte b in bcdNumber)
                {
                    int digit1 = b >> 4;
                    int digit2 = b & 0x0f;
                    result = (result * 100) + digit1 * 10 + digit2;
                }
                return result;
            }
    Ну на ST сами переделывайте. Может и готовое есть. (это C#)

  2. #12

    По умолчанию

    ФБ на ST уже давно выкладывал. Функции аналогично, если надо не побайтно.
    Конвертация времени BCD формата панелей, HEX в DEC и обратно DEC в HEX https://owen.ru/forum/showthread.php...39&page=20#200
    Последний раз редактировалось kondor3000; 11.09.2024 в 09:55.

  3. #13

    По умолчанию

    Рабочий код на питоне для считывания текущий концентрации и порогов , может кому пригодится

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


    Пошел теперь бороться с записью порогов и пгэсов


    Вот код для этого, но до 15 включительно записывается правильное значение , а если я ввожу 20 , то записывается 14, если 26 то 20. Ткните носом где у меня преобразование не правильно происходит?

    [code python]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()

    Мои мысли такие что от 16 мы должны писать уже не только в BIL а и в BH, BL, BIH
    Последний раз редактировалось atomo2; 11.09.2024 в 12:35.

  4. #14
    Пользователь
    Регистрация
    23.09.2008
    Адрес
    Центророссийск
    Сообщений
    3,134

    По умолчанию

    Мои мысли такие что от 16 мы должны писать уже не только в BIL а и в BH, BL, BIH
    почитай про bcd-формат
    и см. п#11

  5. #15

    По умолчанию

    Цитата Сообщение от Валенок Посмотреть сообщение
    почитай про bcd-формат
    и см. п#11
    Спасибо разобрался)

    def split_value_bcd(value):

    bcd_value = int(f"{value:02d}", 16) # Преобразуем в BCD
    # Раскладываем по байтам
    bh = 0
    bl = 0
    bih = (bcd_value >> 8) & 0xFF
    bil = bcd_value & 0xFF

  6. #16
    Пользователь
    Регистрация
    23.09.2008
    Адрес
    Центророссийск
    Сообщений
    3,134

    По умолчанию

    Что разобрались - хорошо. Непонятно зачем на байты раскладывать. Вы п.11 недочитали

Страница 2 из 2 ПерваяПервая 12

Похожие темы

  1. Ответов: 26
    Последнее сообщение: 31.01.2023, 17:42
  2. Ответов: 2
    Последнее сообщение: 04.06.2019, 16:55
  3. Ответов: 10
    Последнее сообщение: 10.06.2018, 16:36
  4. Ответов: 2
    Последнее сообщение: 17.03.2016, 08:47
  5. Ответов: 5
    Последнее сообщение: 14.10.2010, 13:42

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •