Найти - Пользователи
Полная версия: Опрос теплосчетчика по RS485
Начало » Центр помощи » Опрос теплосчетчика по RS485
1
Akhepython
Доброго времени суток
Просьба помочь разобраться с ребусом по опросу теплосчетчика SANEXT Mono RM по RS485
Есть описание формата данных
https://imgur.com/J6SwXrK
https://imgur.com/nOzvkYr
Есть Raspberry pi 3 b и rs485 to usb
И огромное желание разобраться как собрать данные )
Планировал использовать готовую библиотеку minimalmodbus для питона с режимом RTU.
Но не осилил синтаксис. Точнее не осилил в целом логику синтаксиса передачи и получения данных по RS485 даже просто в режиме СОМ порта.
Единственное пока, что понял из найденных примеров что нужно передавать данные в НЕХ. Еще как то нужно CRC16 посчитать и куда то деть. В общем непонятно ни чего от слова совсем.
Примеров под мой случай толковых не нашел. Буду очень признателен за помощь в решении этого ребуса. Мне по сути нужно вытащить один показатель только.

https://github.com/n0l/Mercury_remote/blob/master/get_data_python3.py
нашел очень близкий пример по реализации, но не понимаю части логики кода….
xam1816
 import crcmod
#
def crc16_calc(chunk): # считает crc16
    crc16 = crcmod.mkCrcFun(0x18005, initCrc=0xFFFF, rev=True, xorOut=0x0000)
    crc_int = crc16(chunk)
    high_byte = crc_int >> 8
    low_byte = crc_int & 0xff
    out_hex = '{:02X}{:02X}'.format(low_byte, high_byte)
    return bytes.fromhex(out_hex)
#
def create_package(addr, f, l, mask_ch, id) -> bytes: # собирает пакет
    hex_chunk = '{:08d}{:02X}{:02X}{:<08}{:02X}'.format(addr, f, l, '{:02X}'.format(mask_ch), id)
    chunk = bytes.fromhex(hex_chunk)
    data = chunk + crc16_calc(chunk)
    return data
#
addr = 12345678 # - сетевой адрес устройства (4байта) в формате BCD, старшим байтом вперёд
f = 1 # - код функции запроса (1 байт) чтение текущих показаний
l = 14 # - общая длина пакета (1 байт) 
mask_ch = 2 # – битовая маска запрашиваемых каналов (4 байта) (чтение второго канала)
id = 24228 # - идентификатор запроса (любые 2 байта)
#
data = create_package(addr, f, l, mask_ch, id) # отправить теплосчетчику,
#
print(data) #b'\x124Vx\x01\x0e\x02\x00\x00\x00^\xa4Ac'
py.user.next
Akhepython
Единственное пока, что понял из найденных примеров что нужно передавать данные в НЕХ.
Сначала формировать данные в hex. Потом передавать сформированные данные как байты простые. Потом получать ответ от устройства в виде простых байтов. Потом разбирать эти простые байты на поля и получать данные из этих полей.

Для этого всего хватит pyserial, bytearray(), crcmod и struct.
Demion
Здравствуйте, увидел Ваше сообщения о созданию пакета данных с CRC. Не могли бы Вы помочь в разборе пакета приходящего от счетчика?
Счетчик Пульсар с rs485. Запрос “08 56 16 18 01 0E 03 00 00 00 01 00 FE 71”.
Получаю ответ: “085616180112a3e8103c0000000001005fab”.
Вручную (согласно протокола от Пульсар) разбираю: “08561618” - ADDR (номер счетчика), “01” - код функции запроса, “12” - общая длина пакета, “a3e8103c00000000” - данные, “01 00” - ID (идентификатор запроса (любые 2 байта))
“5fab” - CRC16.
Но как это сделать на Python? Просьба помочь!

Rodegast
> Но как это сделать на Python? Просьба помочь!

Что тебе нужно сделать? CRC16 вычислить? Но в твоём протоколе есть же готовый пример его вычисления…
Demion
Мне нужна помощь в написании кода на питоне для разбора ответа от прибора, например: ответ: “085616180112a3e8103c0000000001005fab”. Но ответ может быть и другой.
py.user.next
Demion
Здравствуйте, увидел Ваше сообщения о созданию пакета данных с CRC. Не могли бы Вы помочь в разборе пакета приходящего от счетчика?
Счетчик Пульсар с rs485. Запрос “08 56 16 18 01 0E 03 00 00 00 01 00 FE 71”.
Получаю ответ: “085616180112a3e8103c0000000001005fab”.
Вручную (согласно протокола от Пульсар) разбираю: “08561618” - ADDR (номер счетчика), “01” - код функции запроса, “12” - общая длина пакета, “a3e8103c00000000” - данные, “01 00” - ID (идентификатор запроса (любые 2 байта))
“5fab” - CRC16.
Но как это сделать на Python? Просьба помочь!
Можешь составить кортеж из частей:
  
>>> def get_data():
...     text = '085616180112a3e8103c0000000001005fab'
...     out = bytes.fromhex(text)
...     return out
... 
>>> def parse_data(data):
...     addr = data[:4]
...     func = data[4:5]
...     code = data[5:6]
...     payload = data[6:-4]
...     ident = data[-4:-2]
...     crc = data[-2:]
...     out = (
...         addr,
...         func,
...         code,
...         payload,
...         ident,
...         crc
...     )
...     return out
... 
>>> data = get_data()
>>> 
>>> data_parsed = parse_data(data)
>>> print(data_parsed)
(b'\x08V\x16\x18', b'\x01', b'\x12', b'\xa3\xe8\x10<\x00\x00\x00\x00', b'\x01\x00', b'_\xab')
>>> 
>>> print(list(data_parsed[0]))
[8, 86, 22, 24]
>>> print(list(data_parsed[1]))
[1]
>>> print(list(data_parsed[2]))
[18]
>>> print(list(data_parsed[3]))
[163, 232, 16, 60, 0, 0, 0, 0]
>>> print(list(data_parsed[4]))
[1, 0]
>>> print(list(data_parsed[5]))
[95, 171]
>>>
Можешь составить словарь из частей:
  
>>> def get_data():
...     text = '085616180112a3e8103c0000000001005fab'
...     out = bytes.fromhex(text)
...     return out
... 
>>> def parse_data(data):
...     addr = data[:4]
...     func = data[4:5]
...     code = data[5:6]
...     payload = data[6:-4]
...     ident = data[-4:-2]
...     crc16 = data[-2:]
...     out = {
...         'address': addr,
...         'function': func,
...         'code': code,
...         'payload': payload,
...         'id': ident,
...         'crc': crc16
...     }
...     return out
... 
>>> data = get_data()
>>> 
>>> data_parsed = parse_data(data)
>>> print(data_parsed)
{'address': b'\x08V\x16\x18', 'function': b'\x01', 'code': b'\x12', 'payload': b'\xa3\xe8\x10<\x00\x00\x00\x00', 'id': b'\x01\x00', 'crc': b'_\xab'}
>>> 
>>> print(list(data_parsed['address']))
[8, 86, 22, 24]
>>> print(list(data_parsed['function']))
[1]
>>> print(list(data_parsed['code']))
[18]
>>> print(list(data_parsed['payload']))
[163, 232, 16, 60, 0, 0, 0, 0]
>>> print(list(data_parsed['id']))
[1, 0]
>>> print(list(data_parsed['crc']))
[95, 171]
>>>
Можешь составить объект из частей:
  
>>> class Data:
...     def __init__(self, addr, func, code,
...                  payload, ident, crc16):
...         self._addr = addr
...         self._func = func
...         self._code = code
...         self._payload = payload
...         self._ident = ident
...         self._crc16 = crc16
...     def __str__(self):
...         t = (self._addr, self._func, self._code,
...              self._payload, self._ident, self._crc16)
...         return '{}{}'.format(__class__.__name__, t)
...     def addr(self):
...         return self._addr
...     def func(self):
...         return self._func
...     def code(self):
...         return self._code
...     def payload(self):
...         return self._payload
...     def ident(self):
...         return self._ident
...     def crc16(self):
...         return self._crc16
... 
>>> def get_data():
...     text = '085616180112a3e8103c0000000001005fab'
...     out = bytes.fromhex(text)
...     return out
... 
>>> def parse_data(data):
...     addr = data[:4]
...     func = data[4:5]
...     code = data[5:6]
...     payload = data[6:-4]
...     ident = data[-4:-2]
...     crc16 = data[-2:]
...     out = Data(
...         addr,
...         func,
...         code,
...         payload,
...         ident,
...         crc16
...     )
...     return out
... 
>>> data = get_data()
>>> 
>>> data_parsed = parse_data(data)
>>> print(data_parsed)
Data(b'\x08V\x16\x18', b'\x01', b'\x12', b'\xa3\xe8\x10<\x00\x00\x00\x00', b'\x01\x00', b'_\xab')
>>> 
>>> print(list(data_parsed.addr()))
[8, 86, 22, 24]
>>> print(list(data_parsed.func()))
[1]
>>> print(list(data_parsed.code()))
[18]
>>> print(list(data_parsed.payload()))
[163, 232, 16, 60, 0, 0, 0, 0]
>>> print(list(data_parsed.ident()))
[1, 0]
>>> print(list(data_parsed.crc16()))
[95, 171]
>>>
Что нужно составлять и что не нужно составлять, зависит от задачи. Задача определяет наилучший вариант.

Например, у тебя есть три инструмента: молоток, шуруповёрт и болгарка. Какой из этих инструментов наилучший, если нужно забить гвоздь? Какой из этих инструментов наилучший, если нужно закрутить шуруп? Какой из этих инструментов наилучший, если нужно разделить железную трубу на части? Какой из этих инструментов наилучший, если нужно разжечь костёр? То есть задача определяет, что хорошо и что плохо в данном конкретном случае, в рамках, границах, в области этой задачи.

Бери вариант со словарём, чтобы не путаться. Внутри словаря этим полям можно давать любые имена.

Также вместо bytes() можно применить bytearray(), так как он даёт больше возможностей для работы с бинарными данными. В нём, в отличие от bytes(), можно менять набор значений, добавлять к ним что-то, удалять из них что-то, менять в них что-то с одного на другое.

Пример:
Это тип bytes(), его можно только читать:
  
>>> data = bytes([0x01, 0x02, 0x03])
>>> data
b'\x01\x02\x03'
>>> 
>>> data[1]
2
>>> 
>>> data[1] = 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'bytes' object does not support item assignment
>>>
Это тип bytearray(), его можно как читать, так и писать:
  
>>> data = bytearray([0x01, 0x02, 0x03])
>>> data
bytearray(b'\x01\x02\x03')
>>> 
>>> data[1]
2
>>> 
>>> data[1] = 4
>>> data
bytearray(b'\x01\x04\x03')
>>> 
>>> data[1]
4
>>>
Если тебе нужно что-то создать (сформировать), пиши функцию для создания. Если тебе нужно что-то разобрать (разделить), пиши функцию для разбора. На каждое действие пиши функцию.
xam1816
Demion
Мне нужна помощь в написании кода на питоне для разбора ответа от прибора, например: ответ: “085616180112a3e8103c0000000001005fab”. Но ответ может быть и другой.

  
import struct
 
def calculate_crc16_modbus(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')
 
def parse(data):
    hex_data = bytes.fromhex(data)
    addr = hex_data[:4]
    f = hex_data[4]
    l = hex_data[5]
    ch = hex_data[6:-4]
    id = hex_data[-4:-2]
    crc = hex_data[-2:]
    if crc != calculate_crc16_modbus(hex_data[:-2]):
        print('bad data')
        return None
 
    out = {}
    out['addr'] = ''.join(f'{byte >> 4}{byte & 0x0F}' for byte in addr)
    out['func'] = int(f)
    out['len'] = int(l)
    out['value'] = out['func'] == 1 and struct.unpack('ff', ch)[0] or None
    out['id'] = id.hex()
    return out
 
data = parse('085616180112a3e8103c0000000001005fab')
print(f"addr: {data['addr']}")
print(f"func_num: {data['func']}")
print(f"byte_received: {data['len']}")
print(f"value: {data['value']}")
print(f"id: {data['id']}")
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB