Найти - Пользователи
Полная версия: MODBUS или заморочки с CRC
Начало » Python для новичков » MODBUS или заморочки с CRC
1 2
CryptSpirit
Для опроса счетчиков используеться протокол MODBUS(это в краце). Загвоздка возникает в том что сам протокол я решил составлять сам(видел есть готовый модуль для питона). И все хорошо но контроль ошибок мне как кость в горле.

Контрольная сумма CRC состоит из двух байт. Контрольная сумма вычисляется передающим устройством и добавляется в конец сообщения. Принимающее устройство вычисляет контрольную сумму в процессе приема и сравнивает ее с полем CRC принятого сообщения.
Счетчик контрольной суммы предварительно инициализируется числом FF hex. Только восемь бит данных используются для вычисления контрольной суммы CRC. Старт и стоп биты, бит паритета, если он используется, не учитываются в контрольной сумме.
Во время генерации CRC каждый байт сообщения складывается по исключающему ИЛИ с текущим содержимым регистра контрольной суммы. Результат сдвигается в направлении младшего бита, с заполнением нулем старшего бита. Если младший бит равен 1, то производится исключающее ИЛИ содержимого регистра контрольной суммы и определенного числа. Если младший бит равен 0, то исключающее ИЛИ не делается.
Процесс сдвига повторяется восемь раз. После последнего (восьмого) сдвига, следующий байт складывается с текущей величиной регистра контрольной суммы, и процесс сдвига повторяется восемь раз как описано выше. Конечное содержание регистра и есть контрольная сумма CRC

Собственно вопросы:
1 Есть пример вычисления CRC на вики но немогу разобраться с тем какой именно мне алгоритм нужен (думаю что CRC8). Да и код на си прочетать не могу (вобщем коды С читаю но этот что то непошол). Помогите с переводом на питон
2 Просьба кто работал на питоне с этим протоколом объясните по возможности что и как (ссылкам на летературу тоже буду рад)

Заранее благодарен.
igor.kaist
CryptSpirit
Помогите с переводом на питон
Да много примеров и на питоне можно найти. Гугл: “crc8 python”:
https://www.cgran.org/browser/projects/ucla_zigbee_phy/branches/cwna06/src/python/crc8.py?rev=42
CryptSpirit
CryptSpirit
Контрольная сумма CRC состоит из двух байт
В примере к прибору по байтам описано “CRCL, CRCH”. Кто из них кто? Я только недавно взялся за это задание и сроки поджимают. Прошу помощи. Объясните как из полученого CRC получить CRCL, CRCH, либо расчитать
PooH
CryptSpirit
CryptSpirit
Контрольная сумма CRC состоит из двух байт
В примере к прибору по байтам описано “CRCL, CRCH”. Кто из них кто? Я только недавно взялся за это задание и сроки поджимают. Прошу помощи. Объясните как из полученого CRC получить CRCL, CRCH, либо расчитать
ну явно же L- low H-high, т.е. CRCH старший байт полученной суммы CRCL-младший
CryptSpirit
PooH
ну явно же L- low H-high, т.е. CRCH старший байт полученной суммы CRCL-младший
Спасибо. Припекло и сам догадался
pyuser
года три назад писал програмулину для опроса прибора по этому протоколу. контрольную сумму считал так: (алгоритм, понятно, не мой, взят из С-шных примеров)
#~ Table of CRC values for high–order byte
auchCRCHi = [
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40]

#~ Table of CRC values for low–order byte
auchCRCLo = [
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40]
##########################################################################

def crc16 (data) :
uchCRCHi = 0xFF # high byte of CRC initialized
uchCRCLo = 0xFF # low byte of CRC initialized
uIndex = 0x0000 # will index into CRC lookup table

for ch in data :
uIndex = uchCRCLo ^ ord(ch)
uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex]
uchCRCHi = auchCRCLo[uIndex]
return (uchCRCHi << 8 | uchCRCLo)
CryptSpirit
Спосибо. На днях будет возможность обкатать то что я нацарапал. И ваш пример попробую.
CryptSpirit
pyuser
года три назад писал програмулину для опроса прибора по этому протоколу
pyuser, у меня загвоздка. либо я неправильно код написал либо прибор неправильно подключили. Если можно выложите 100% рабочий код что бы можно было определить где я ошибся. Заранее спасибо.
CryptSpirit
import serial
import time
import crc16pythonsu
import struct

def ECHO(data):
com = serial.Serial(port='COM3', baudrate=19200, timeout=10, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS)
retCRC = crc16pythonsu.crc16(struct.pack("%dB" % len(data), *data))
data.append(retCRC[0])
data.append(retCRC[1])
data = struct.pack("%dB" % len(data), *data)
com.write(data)
tout = 50
out = ''
while tout != 0:
tout -= 1
while com.inWaiting() > 0:
out += com.read(1)
return out

for i in range(10):
y = ECHO([0, 4, 0, 9, 0, 1])
print len(y)
for i in range(len(y)):
print ord(y[i]),
print
time.sleep(2)
Возможно я где то что то делаю не так. По Com порту еще не подключал приборы. Помогите разобраться есть ли в коде ошибки
pyuser
CryptSpirit
выложите 100% рабочий код
девайс назывался МАК2000
import os, sys
import struct

import time as _time

from crc16 import crc16
from logutils import class_logger
#####################################################################################

errors = {
0x02 : "Ошибка RTC",
0x03 : "Ошибка DIO",
0x04 : "Недостаток памяти",
0x05 : "Ошибка DATAFLASH",
0x10 : "Ошибка ОЗУ",
0x20 : "Ошибка ПЗУ",
0x30 : "Ошибка EEPROM"
}
#####################################################################################

class mak_error (RuntimeError):
pass

class mak_item (object) :
def __init__ (self, id, query, crc, multiplier) :
self.id = id
self.query = query
self.crc = crc
self.multiplier = multiplier
self.value = 0

class mak2k (object) :

__logger = class_logger()

def __init__ (self, serial, device_id) :
assert serial is not None, "Не инициализирован COM-порт"
self.__serial = serial
self.device_id = device_id
self.__registers = {}

## регистры данных начинаются с адреса 40018 (40018 - 40001 = 17),
## всего 8 регистров
self.__group_query = struct.pack(">2B2H", device_id, 3, 17, 8)
self.__group_crc = struct.pack("=H", crc16(self.__group_query))

## регистр статуса устройства 40016 (40016 - 40001 = 15)
query = struct.pack(">2B2H", device_id, 3, 15, 1)
crc = struct.pack("=H", crc16(query))

self.__status = mak_item(0, query, crc, 1)
self.err_string = ""

@property
def registers (self) :
return self.__registers

def append (self, item_id, item_register, item_mult) :
item_mult = 1.0/item_mult
if item_mult == 1.0 : item_mult = int(item_mult)

query = struct.pack(">2B2H", self.device_id, 3, item_register - 40001, 1)
crc = struct.pack("=H", crc16(query))
self.__registers[item_register] = mak_item(
item_id, query, crc, item_mult
)

def __get (self, item) :
self.__serial.flushOutput()
_time.sleep(0.2)
self.__serial.write(item.query)
self.__serial.write(item.crc)
_time.sleep(0.2)

data = self.__serial.read(7)
self.__serial.flushInput()
self.__logger.debug(u"Device %d: query: %s, result: %s", self.device_id, `item.query`, `data`)
if len(data) == 7 :
data = struct.unpack(">3B2H", data)[3]
else :
self.__logger.debug(u"Device %d: прочитал %d байт (ожидалось 7)", self.device_id, len(data))
data = None
return data

def read (self) :
self.err_string = ""
if not self.__serial.isOpen() :
try :
self.__serial.open()
except :
self.err_string = str(sys.exc_info()[1])
return False

try :
status = self.__get(self.__status)
if status is not None :
if (status & 0x8000) == 0x8000 :
err_code = status & 0x000F
self.err_string = errors[err_code]
return False
if (status & 0x4000) == 0 :
self.err_string = "Прибор не в режиме измерения"
return False
else:
self.err_string = "Device %d: ошибка чтения статуса устройства" % self.device_id
return False
_time.sleep(1)

for item in self.__registers.values() :
value = self.__get(item)
if value is not None :
item.value = value * item.multiplier
_time.sleep(1)
except :
self.err_string = str(sys.exc_info()[1])
return False
return True

def group_read (self) :
self.err_string = ""
if not self.__serial.isOpen() :
try :
self.__serial.open()
except :
self.err_string = str(sys.exc_info()[1])
return False

try :
status = self.__get(self.__status)
if status is not None :
if (status & 0x8000) == 0x8000 :
err_code = status & 0x000F
self.err_string = errors[err_code]
return False
if (status & 0x4000) == 0 :
self.err_string = "Прибор не в режиме измерения"
return False
_time.sleep(1)

self.__serial.flushOutput()
_time.sleep(0.2)
self.__serial.write(self.__group_query)
self.__serial.write(self.__group_crc)
_time.sleep(1)

data = self.__serial.read(3)
cnt = struct.unpack("=3B", data)[2]
data = self.__serial.read(cnt)
self.__serial.read(2)
self.__serial.flushInput()
self.__logger.debug(u"Device %d: query: %s, result: %s, count: %d", self.device_id, `self.__group_query`, `data`, cnt)
if len(data) != cnt :
self.__logger.debug(u"Device %d: прочитал %d байт (ожидалось %d)", self.device_id, len(data), cnt)
data = None
elif cnt == 1 :
self.__logger.debug(u"Device %d: прочитал %d байт (ожидалось 2)", self.device_id, len(data))
data = None

if data is None :
self.err_string = u"Прочитано не верное число байт"
return False

try :
values = struct.unpack(">%dH" % (cnt/2), data)
except :
raise mak_error("CRC Error")

for i, value in enumerate(values) :
idx = 40018 + i
if idx in self.__registers :
item = self.__registers[idx]
item.value = value * item.multiplier
except :
self.err_string = str(sys.exc_info()[1])
return False
return True
#################################### End Of File ####################################
код модуля crc16 в этом топике
код модуля logutils взят с этого форума - Андрей Светлов показывал пример использования дескрипторов
инициализация СОМ-порта:
self._port     = 0
self._baudrate = 9600
self._bytesize = serial.EIGHTBITS
self._stopbits = serial.STOPBITS_ONE
self._timeout = 3.0
# port, baudrate, timeout - задаются в настройках
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