LINUX.ORG.RU

Помогите с расчетом CRC8 суммы

 ,


0

2

Поможите люди добрые! Сами мы не местные (не ассемблерщики и вообще не очень умные, как видно).

Есть весы, работающие по протоколу Тензо М. В описании протокола для расчета CRC суммы приведен вот такой код на ассемблерной вставке якобы для C++

BYTE CDeviceTestDlg::CRCMaker(BYTE b_input, BYTE b_CRC)
{
__asm
{
  mov al,b_input
  mov ah,b_CRC
  mov cx,8
mod1:
  rol al,1
  rcl ah,1
  jnc mod2
  xor ah,69h
mod2:
  dec cx
  jnz mod1
  mov b_CRC,ah
}
return b_CRC;

Мать их, 16 битный ассемблер в 2021!!! Да чтоб они так жили.

В эту функцию, насколько я понял, надо подставить байты пакета по очереди и в итоге на последнем байте получится CRC. Как это сделать толком тоже не понятно, но ладно - мне бы сначала просто скомпилировать эту функцию! Подскажите, как вообще при помощи gcc собрать такой код на современной системе?

Что делал. В лоб конечно не собирается. Добавлял .code16, .intel_syntax noprefix - ругается все равно что таких инструкций асма нет.

Пример гарантированно правильных кадров с верной CRC суммой, сдамплено сниффером с фирменной утилиты

0xFF 0x02 0xC3 0xE6 0xFF 0xFF
0xFF 0x01 0xC3 0xE3 0xFF 0xFF

Третий с конца байт - это CRC. 0xFF - разделители, и вот тут вообще не понятно, надо ли их пихать в расчет контрольной суммы. Если кто имеет опыт с протоколом Тензо М, подскажите пожалуйста.

Но основной вопрос - что делать с ассемблерной вставкой.

Выдрал с форума и накорячил вариант на C, но не могу понять - он соответствует ассемблерному коду в ОП или нет. Сумму правильно не считает

unsigned char Crc8(unsigned char * pcBlock, unsigned char len)
{
  unsigned char crc = 0x00;
  unsigned char i;

  while (len--)
  {
    crc ^= *pcBlock++;

    for (i = 0; i < 8; i++)
      crc = crc & 0x80 ? (crc << 1) ^ 0x69 : crc << 1;
  }
  return crc;
}
James_Holden ★★★
() автор топика

В основном у меня все уперлось в этот кусок

rol al,1
rcl ah,1
jnc mod2

не могу сообразить, какому коду на C это соответствует?

James_Holden ★★★
() автор топика
Ответ на: комментарий от anonymous

судя по асму там pcBlock (b_input, al) вообще не используется.

Используется вроде, я так понимаю что инструкция

rol al,1

ставит CF флаг в определенном случае и так влияет на jnc. То есть флаг CF может быть поставлен строчкой

rol al,1

а может следующей за ней

rcl ah,1
James_Holden ★★★
() автор топика

Перепиши на C/C++. У них есть пояснения.

При формировании CRC используется примитивный неприводимый порождающий полином в 9-й степени P(X)-101101001b (169h). На передающей стороне в конце массива используется нулевой байт (00h). Подставляя в переменную b_input байты массива, включая ну- левой байт, вычисляется CRC код с помощью подпрограммы CRCMaker. При передаче массива нулевой байт заменяется вычисленным байтом CRC. На принимающей стороне вычисляют CRC, подставляя в b_input байты принятого массива, включая принятый CRC код. Если вычисленный CRC будет равен нулю, то массив принят правильно. В начале приема/передачи перед вычислением CRC в переменную b_CRC записывается ноль.

Также надо иметь в виду, что экранирующий символ 0xFE учитываться не должны. Этот символ вставляется при передаче после расчета CRC и до расчета CRC на приемной стороне

Если в поле расширенного адреса кода операции, данных или CRC встречается FFh, то на передающем конце после него вставляется код FEh, а на приемном конце он выбрасывается. По вставленному и выброшенному FEh CRC не вычисляется.

Zubok ★★★★★
()
Ответ на: комментарий от James_Holden

Да, используется, rol ставит флаг (из старшего бита инпута), rcl его «вдвигает» в crc справа и одновременно «выдвигает» в него бывший старший бит crc.

Давайте просто тупо повторим это на Си:

#include <stdint.h>

uint8_t crc8(uint8_t input, uint8_t crc)
{
  for (int i=0; i<8; ++i) {
    int major = (crc & 0x80);
    crc <<= 1;
    if ((input & (1<<(7-i)))!=0)
      crc |= 1;
    if (major)
      crc ^= 0x69;
  }
  return crc;
}

теперь поэкспериментируем с вашими данными:

int main(void)
{
  uint8_t crc = 0x00;
  crc = crc8(0x02, crc);
  crc = crc8(0xc3, crc);
  crc = crc8(0xe6, crc);
  printf("%02x\n",crc);
  return 0;
}

Получили 0, его же получили на наборе 0x01, 0xc3, 0xe3. Ага, что-то начинает просматриваться (т.е. как ПРОВЕРИТЬ контрольную сумму, мы уже знаем).

Теперь попробуем сформировать:

int main(void)
{
  uint8_t crc = 0x00;
  crc = crc8(0x02, crc);
  crc = crc8(0xc3, crc);
  crc = crc8(0x00, crc); // здесь 0 вместо crc8, которого мы не знаем
  printf("%02x\n",crc);
  return 0;
}

Получили e6 и радуемся.

LeninGad
()
Ответ на: комментарий от Zubok

Также надо иметь в виду, что экранирующий символ 0xFE учитываться не должны. Этот символ вставляется при передаче после расчета CRC и до расчета CRC на приемной стороне

Вставляется только если надо экранировать 0xFF, который служит разделителем. У меня так получается, что я не попадаю на такие случаи, поэтому пока ничего не вставляю.

используется примитивный неприводимый порождающий полином в 9-й степени P(X)-101101001b

Я лично не могу по этому пояснению вообще ничего понять, как что должно считаться. Есть какой-нибудь стандартный код расчета CRC по «неприводимый порождающий полином в 9-й степени P(X)-101101001b»?

James_Holden ★★★
() автор топика
Ответ на: комментарий от LeninGad

О! Отлично, вот спасибо!

Это то что нужно. Судя по всему все работает теперь.

James_Holden ★★★
() автор топика
Ответ на: комментарий от James_Holden

rcl затирает предыдущее значения флага, поэтому rol al,1 ни на что не влияет. может быть al где-то в вызывающем коде используется? какое там соглашение вызовов? возвращаемое значение в стеке или в ax?

я так понимаю этот асм:

unsigned char CRCMaker(unsigned char b_CRC) {
    for (int i=0; i<8; i++) {
        if (b_CRC & 0x80) 
            b_CRC ^= 0x69;
        b_CRC <<= 1;
    }
    
    return b_CRC;
}
anonymous
()
Ответ на: комментарий от LeninGad

Да, используется, rol ставит флаг (из старшего бита инпута), rcl его «вдвигает» в crc справа и одновременно «выдвигает» в него бывший старший бит crc

а, точняк

anonymous
()
Ответ на: комментарий от anonymous

Нет, такого кода быть никак не может - контрольная сумма по данным считается. Они (input_b) не могут не влиять.

James_Holden ★★★
() автор топика

Что делал. В лоб конечно не собирается. Добавлял .code16, .intel_syntax noprefix - ругается все равно что таких инструкций асма нет.

Все правильно, в 64-х битном (ты в нем наверное работаешь?) 16-ти битный код в принципе не поддерживается. По этой причине, например, в отличие от 32-х битной винды, в 64 битной старые dos-программы или win16 могут только в эмуляторе работать, а не нативно.

anonymous
()
Ответ на: комментарий от anonymous

Понятно, но тут больше вопрос в том - можно ли вот такой древний код скомпилировать под 64 битную систему, чтобы автоматически инструкции преобразовались в 64 битные. То есть мне не нужен 16-битный бинарник, естественно.

James_Holden ★★★
() автор топика
Ответ на: комментарий от James_Holden

Конечно можно.

unsigned char crc(unsigned char b_input, unsigned char b_CRC)
{
  asm (
    "mov        %%al, %1        \n"
    "mov        %%ah, %2        \n"
    "mov        $8, %%cx        \n"
    "mod1:                      \n"
    "rol        $1, %%al        \n"
    "rcl        $1, %%ah        \n"
    "jnc        mod2            \n"
    "xor        $0x69, %%ah     \n"
    "mod2:                      \n"
    "dec        %%cx            \n"
    "jnz        mod1            \n"
    "mov        %%ah, %0"

    : "=r" (b_CRC)
    : "r" (b_input), "r" (b_CRC)
  );

  return b_CRC;
}
bigbit ★★★★★
()
Ответ на: комментарий от James_Holden

GNU as всё прожуёт при .intel_syntax noprefix, будет вам 64-битный код (анонимус выше прав только в том смысле, что в 64-битном режиме нет 16-битных сегментов, где бывает mov cx,.. без префиксов – но это не мешает из 64-битного кода работать в том числе с cx).

Только это щас будет уныло: сначала мы это вынесем в отдельный юнит, потом будем обмазывать дополнительным кодом для соответствия x86-64 sysv abi, получим непортабельность на другие платформы (в частности, на винду) реально на пустом месте… Если вы не доверяете переписанному фрагменту в моей версии, давайте это проделаем, что ж.

Правда, мне кажется, вам лучше попробовать поделить многочлены над Z_2 столбиком на бумажке, пока не станет понятно, откуда там xor и что это всё значит вообще. Тогда компонент «магии» из кода изчезнет и будет сразу понятно, что происходит.

LeninGad
()
Ответ на: комментарий от James_Holden

Главное, компилируется.
Ты получил 16-битный код в своем 64-битном бинарнике.
А дальше ты уже поправишь.

bigbit ★★★★★
()
Последнее исправление: bigbit (всего исправлений: 1)
Ответ на: комментарий от LeninGad

Если вы не доверяете переписанному фрагменту в моей версии

Доверяю, я проверил на других данных - все отлично считает. Конкретно этот вопрос по весам можно считать закрытым, просто я на будущее хочу разобраться как с ассемблером работать, если припрет.

вам лучше попробовать поделить многочлены над Z_2 столбиком на бумажке

Я знаю как работает CRC, просто ассемблер не знал да еще забыл, и было подозрение - вдруг у них там какая нестандартная (с точки зрения CRC) фигня в алгоритме.

James_Holden ★★★
() автор топика
Ответ на: комментарий от LeninGad

GNU as всё прожуёт при .intel_syntax noprefix

Вот у меня не прожевывало почему-то, наверное потому что я имена переменных прямо в ассемблерном коде оставлял, вместо подстановок. А я неверно подумал что он сами инструкции не жует. Оказалось все намного лучше.

James_Holden ★★★
() автор топика
Ответ на: комментарий от James_Holden

В первых двух строках перепутан порядок операндов, если поправить, тогда всё приблизительно работает (на самом деле нет, с gcc -O2 становится заметно, как оно ломается: метки должны быть локальными, и constraints на регистрах надо усилить, чтоб оно не пыталось генерировать mov %dil, %ah, и ещё clobbered-регистры прописать, которые в нашей ситуации, к счастью, ничего не ломают, но это чистое везение).

LeninGad
()
Ответ на: комментарий от LeninGad

То есть, -O2 оптимизирует и ассемблерные вставки тоже? Я честно думал что нет, а если так, то лично мне лучше с этим не связываться и делать на C, даже если припрет.

Глубоко изучать ассемблер я не готов по роду своих задач.

James_Holden ★★★
() автор топика
Ответ на: комментарий от James_Holden

Нет, -О2 делает больно по-другому. Он влияет на то, что происходит «на стыке» ассемблерных вставок и окружающего кода (в данном примере он пытается достать аргумент из регистра %dil, который внезапно нельзя взять и переложить в %ah), ну и на то, что код повторится столько раз, сколько содержащая функция заинлайнится (или там при loop unroll тоже; в данном примере от этого возникает проблема с нелокальными метками, которые не имеют права повторяться).

LeninGad
()

Я писал реализацию протокола Тензо-М для ПЛК Siemens. У них есть код на C в официальной документации. Я его на SCL переводил

https://www.tenso-m.ru/pdf/vks.pdf

//~~~~~~~~~~~~~~~~~~~~~~~~~~
// Эта функция рассчитывает контрольную сумму
// последовательности байтов и возвращает результат.
unsigned char ucCrcMaker (unsigned char *InputData, unsigned char BytesNumber, unsigned char Offset)
//    *InputData -указатель на последовательность байтов.
//    BytesNumber -количество байтов, 
// для которых считается контрольная сумма.
//    Offset -смещение относительно начала последовательности
// байтов, с которого начинает считаться контрольная сумма 
// (0 -без смещения).
//    Возвращает контрольную сумму.
{
	// Служебные параметры.
	register unsigned char i, j, Data, CrcCode = 0, Polinom = 0x69;
	for(i = Offset; i < BytesNumber +Offset; i++)
	{
		Data = InputData[i];
		for(j = 0; j < 8; j++)
		{
			if(CrcCode & (1 << 7))
			{
				CrcCode *= 2;
				if(Data & (1 << 7)) 
				CrcCode ++;
				CrcCode ^= Polinom;
			}
			else // if(CrcCode & (1 << 7))
			{
				CrcCode *= 2;
				if(Data & (1 <<7)) 
					CrcCode ++;
			}    // if(CrcCode & (1 << 7))
			Data *= 2;
		} // for(j = 0; j < 8; j++) 
	}  // for(i = Offset; i < BytesNumber + Offset; i++)
	// Вернѐм контрольную сумму.
	return CrcCode;
}  // unsigned char ucCrcMaker ( ... )
//~~~~~~~~~~~~~~~~~~~~~~~~~~
Alden ★★★★
()
Последнее исправление: Alden (всего исправлений: 1)

Вот мой код на SCL (сименсовском диалекте языка ST)

#done := false;
#CRC_code := 0;
//Обход входного массива
#i := #start_addr;
WHILE #i < #start_addr + #msg_length DO
    //Берём i-й байт
    #data := #buffer[#i];
    //Цикл обработки i-го байта
    #j := 0;
    
    WHILE #j < 8 DO
        IF (#CRC_code & 128) > 0 THEN
            #CRC_code := #CRC_code * 2;
            IF (#data & 128) > 0 THEN
                #CRC_code := #CRC_code + 1;
            END_IF;
            #CRC_code := #CRC_code XOR #polynom;
        ELSE
            #CRC_code := #CRC_code * 2;
            IF (#data & 128) > 0 THEN
                #CRC_code := #CRC_code + 1;
            END_IF;
        END_IF;
        #data := #data * 2;
        #j := #j + 1;
    END_WHILE; //окончание обработки i-го байта
    #i := #i + 1;
END_WHILE; //Окончание обхода входного массива
#CRC := #CRC_code;
#done := true;

Параметры функционального блока:

Input								
		buffer	        Array[0..255] of Byte
		start_addr	Int = 0	
		msg_length	Int = 0
Output								
		done	        Bool = false	
		CRC	        Byte = 16#0	
Constant
                polynom         Byte = 16#69 


Alden ★★★★
()
Последнее исправление: Alden (всего исправлений: 4)
30 июня 2021 г.
Ответ на: комментарий от Alden

Вот мой полностью рабочий проверенный код на Vb.Net

    'Расчёт CRC входного массива байтов. Последний байт должен быть равен 0, т.к., типа на его место встанет наша CRC в последующем отправленном пакете
    Public Function GetCRC(ByRef MassivIn() As Byte) As Byte
        'Расчётный CRC
        GetCRC = 0

        'Примитивный неприводимый порождающий полином в 8-й степени P(X)-101101001b (младшая часть 69h)
        Dim Polynom As Byte = 105 '0x69

        'Обход входного массива
        Dim i As Integer = 0
        For i = 0 To UBound(MassivIn)
            'Берём i-й байт
            Dim Data As Byte = MassivIn(i)

            '8 раз сдвигаем наш байт и XOR-им его. Спасибо https://www.linux.org.ru/forum/development/16306501
            Dim j As Integer = 0
            For j = 0 To 7
                If (GetCRC And 128) > 0 Then
                    GetCRC = (GetCRC << 1)
                    If (Data And 128) > 0 Then GetCRC = GetCRC + 1 'Проверяем самый левый бит. Если он есть - делаем +1 к CRC
                    GetCRC = GetCRC Xor Polynom
                Else
                    GetCRC = (GetCRC << 1)
                    If (Data And 128) > 0 Then GetCRC = GetCRC + 1 'Проверяем самый левый бит. Если он есть - делаем +1 к CRC
                End If
                Data = (Data << 1)
            Next j
        Next i
    End Function
aaaSashaMGGU
()

Можно ещё взять FASM, выдрать этот код в полноценную функцию и слинковать объектник с остальным кодом

Harald ★★★★★
()
Ответ на: комментарий от Harald

Подвох в том, что у меня все там на питоне, и должно работать кроссплатформенно включая винду.

Собственно благодаря этому треду все уже несколько месяцев работает на производстве, без нареканий.

James_Holden ★★★
() автор топика
Ответ на: комментарий от James_Holden

ну дак и FASM кроссплатформенный (в рамках x86 архитектуры), может под линукс сконпелять, может под винду

Harald ★★★★★
()
Ответ на: комментарий от Harald

Неудобно перекомпиливать, а на винде вообще пошло оно нафиг. Проще тупо на питоне сделать функцию было.

James_Holden ★★★
() автор топика
Ответ на: комментарий от James_Holden

Help

Приветствую

Не могу наладить сообщение с этими терминалами. Желательный способ общения через сокеты. В идеале хотелось бы понимать как вручную формировать запрос, чтобы можно было отправлять его через netcat, например.

Есть пример того, как формировать сообщения на python? Есть функция для подсчета CRC на python?

Буду бесконечно благодарен, если есть возможность поделиться кодом на эту тему.

LemonTree
()
Ответ на: Help от LemonTree

Постараюсь поподробнее описать.

Сначала о железе, на всякий случай.

Весы в цеху, от них идет кабель RS-232 в лабораторию, втыкается в искрозащитный барьер от весов, далее идет кабель RS-232, втыкается в преобразователь интерфейсов MOXA nPort.

MOXA по ethernet связан с сервером. В моем случае, со стороны монтажа было 100500 ошибок, в распайке кабелей, еще какой-то хрени. После перепроверки всего по документации на весы и MOXA, прозвона кабелей и проверки RS-232 в эхо режиме перемычкой между 3 и 4 ножкой разъема, перепайки кабелей я стал уверен что железная часть работает.

Далее - я проверял настройки весов, которые делаются через их меню. Там задавал скорость обмена, настройки UART протокола. Тоже могут быть сюрпризы.

Теперь о программной части. Поскольку все работает через ethernet - RS-232 преобразователь, то работа идет через TCP сокет. Каждому RS-232 порту на MOXA соответствует свой номер TCP порта, и тут важно не перепутать! А также, в моем случае MOXA преобразователей было несколько и все постоянно путали какому IP адресу соответствует какой преобразователь.

Вот код python, который получает массу нетто с весов.

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  s.settimeout(3)
  s.connect((weigher_ip, int(weigher_port)))
  if self.id == 4:
    s.sendall(b'\xFF\x01\xC2\x8A\xFF\xFF')
  elif self.id == 5:
    s.sendall(b'\xFF\x02\xC2\x8F\xFF\xFF')

  time.sleep(0.1)
  data = s.recv(1024)
  massa_weigher_get = (data[3] & 0x0F) + ((data[3] & 0xF0) / 16) * 10 + (data[4] & 0x0F) * 100 + ((data[4] & 0xF0) / 16) * 1000 + (data[5] & 0x0F) * 10000 + ((data[5] & 0xF0) / 16) * 100000

  steady_weigher = data[6] & 0b00010000

  commapos = data[6] & 0b00000111

  if steady_weigher:
    massa_weigher = massa_weigher_get / (10 ** commapos) *1000

Вот этот запрос к весам

if self.id == 4:
    s.sendall(b'\xFF\x01\xC2\x8A\xFF\xFF')
  elif self.id == 5:
    s.sendall(b'\xFF\x02\xC2\x8F\xFF\xFF')

такой, потому что у меня двое весов с адресами 1 и 2. Получается два разных запроса с разной контрольной суммой.

А теперь ВНИМАНИЕ! Вот эта строчка

time.sleep(0.1)

очень важна! Если не сделать паузу после запроса к весам, нифига не работало. Я так и не понял почему, видимо я не настоящий сварщик )) Но вот так.

steady_weigher это бит «устаканивания» веса.

По контрольной сумме сейчас в следующем посте.

James_Holden ★★★
() автор топика
Последнее исправление: James_Holden (всего исправлений: 1)
Ответ на: Help от LemonTree

С контрольной суммой, в итоге я сколхозил и захардкодил ее в запросах к весам, то есть, как видишь в моем коде выше, в запросах уже подставлена правильная заранее посчитанная контрольная сумма.

В ответах я контрольную сумму не проверяю, и пока работает без нареканий.

Вот код на C, он точно правильный, который считает контрольную сумму запроса.

#include <stdio.h>
#include <stdint.h>

uint8_t crc8(uint8_t input, uint8_t crc)
{
  for (int i=0; i<8; ++i) {
    int major = (crc & 0x80);
    crc <<= 1;
    if ((input & (1<<(7-i)))!=0)
      crc |= 1;
    if (major)
      crc ^= 0x69;
  }
  return crc;
}

int main()
{
  unsigned char CRC = 0;
  unsigned char data[3] = {0x02, 0xC2, 0x00};

  CRC = crc8(data[0], CRC);
  CRC = crc8(data[1], CRC);
  CRC = crc8(data[2], CRC);

  printf("CRC = %x\n", CRC);
}

В массив data пихаем содержимое пакета запроса (это два байта, и 00 на месте байта контрольной суммы), но без всех этих FF, они не участвуют. Получаем контрольную сумму, которую надо подставить вместо 00 в пакет.

James_Holden ★★★
() автор топика
Ответ на: комментарий от James_Holden

Огромное спасибо за развернутый ответ! От действительно был полезен.

Поставив sleep(0.1), мне удалось единожды получить ответ от весов. Этот факт уже позволил убедиться, что железная часть подключена верно.

Получив 1 ответ, далее к сожалению ответы больше не пришли. Конвертер использую USR-TCP232-302.

Впервые в нем для терминалов Тензо-М увидел, что отправлен ответ от терминала. TX Count/RX Count:4/ 120 bytes.

LemonTree
()
Ответ на: комментарий от James_Holden

Интервал между успешными ответами

Есть ли какой-то параметр ограничивающий частоту ответа терминала? Я могу успешно получить ответ от терминала, но лишь иногда. Не понимаю от чего конкретно зависит это иногда.

Вот пример кода

import time
import socket
host = '192.168.1.7' 
port = 32

def get():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.settimeout(3)
        s.connect((host, int(port)))
        s.sendall(b'\xFF\x01\xC3\xE3\xFF\xFF')

        time.sleep(0.1)
        data = s.recv(1024)
        massa_weigher = (data[3] & 0x0F) + ((data[3] & 0xF0) / 16) * 10 + (data[4] & 0x0F) * 100 + ((data[4] & 0xF0) / 16) * 1000 + (data[5] & 0x0F) * 10000 + ((data[5] & 0xF0) / 16) * 100000

        steady_weigher = data[6] & 0b00010000

        commapos = data[6] & 0b00000111

        if steady_weigher:
            massa_weigher = massa_weigher / (10 ** commapos) *1000
    print('data: ', data, 'w: ',massa_weigher)
    return massa_weigher

while 1:
    time.sleep(1)
    print(time.strftime("%H:%M:%S"))
    try:
        get()
    except Exception:
        print('___') 

Вот что в итоге получается. Есть ли идеи почему терминал в рандомное время готов ответить на запрос, а в другое время не отвечает?

17:05:21
___
17:05:25
___
17:05:29
___
17:05:33
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:05:34
___
17:05:39
___
17:05:43
___
17:05:47
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:05:48
___
17:05:52
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:05:53
___
17:05:57
___
17:06:01
___
17:06:06
___
17:06:10
___
17:06:14
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:06:15
___
17:06:19
___
17:06:23
___
17:06:27
___
17:06:31
___
17:06:36
___
17:06:40
___
17:06:44
___
17:06:48
___
17:06:52
___
17:06:56
___
17:07:00
___
17:07:05
___
17:07:09
___
17:07:13
___
17:07:17
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:07:18
___
17:07:22
___
17:07:26
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:07:27
___
17:07:32
___
17:07:36
___
17:07:40
___
17:07:44
___
17:07:48
___
17:07:52
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:07:53
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:07:54
___
17:07:59
___
17:08:03
___
17:08:07
___
17:08:11
___
17:08:15
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:08:16
___
17:08:20
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:08:21
___
17:08:25
___
17:08:30
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:08:31
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:08:32
___
17:08:36
___
17:08:40
___
17:08:44
___
17:08:48
___
17:08:53
___
17:08:57
___
17:09:01
___
17:09:05
___
17:09:09
___
17:09:13
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:09:14
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:09:15
___
17:09:19
___
17:09:24
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:09:25
___
17:09:29
___
17:09:33
___
17:09:37
___
17:09:41
___
17:09:45
___
17:09:50
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:09:51
___
17:09:55
___
17:09:59
data:  b'\xff\x01\xc3\x00\x00\x000,\xff\xff' w:  0.0
17:10:00
___
17:10:04
___
17:10:08
___
17:10:12
___
17:10:16
LemonTree
()
Ответ на: Интервал между успешными ответами от LemonTree

Есть ли какой-то параметр ограничивающий частоту ответа терминала?

На самом терминале я не нашел такого.

Вот что в итоге получается. Есть ли идеи почему терминал в рандомное время готов ответить на запрос, а в другое время не отвечает?

Трудно сказать, вот с причиной необходимости этой задержки на 0.1 секунду я так и не разобрался. Это было просто экспериментально подобрано. Возможно, на другом терминале надо другую задержку.

Или возможно, что сообщение проходит по кабелю нестабильно, и контрольная сумма не всегда совпадает.

У меня с моим кодом отвечает стабильно, каждый раз.

Есть стандартная программа под винду, от этих терминалов, которая опрашивает их по ком порту и выводит в окно вес. Я использовал ее и смотрел через сниффер, что она шлет.

И - она постоянно вот именно такой запрос посылает, раз за разом. И терминал каждый раз отвечает.

Могу предложить проверить, используя эту программу и сниффер. Если с ней будет работать стабильно, то можно дальше копать на предмет задержек.

Прога https://m.tenso-m.ru/files/Setup_ControllerFree_v.3.2.exe

James_Holden ★★★
() автор топика
Ответ на: комментарий от James_Holden

Помогите с контрольной суммой, ПД308 от Тензо-М

Здравствуйте. Есть прибор ПД308 от Тензо-М, он непрерывно шлет посылку 22 байта: 255 3 204 151 15 15 16 0 0 0 0 0 0 0 0 0 0 0 0 70 255 255,(1 - разделитель, 2 - адрес, 3 - команда, 4-19 - коды ацп, 20 -crc, 21-22 - разделитель, много нулей это потому что подключены 2 датчика из 8) пытаюсь рассчитывать CRC для проверки целостности пакетов. вот мой код на C# (писал по примеру который есть на сайте Тензо-М да и в этой ветке он выше описан с пометкой рабочий):

private static int getCRC(byte[] InputData, int BytesNumber, int Offset) { int i, j;

        byte Data, CrcCode = 0, Polinom = 105; 

        for (i = Offset; i < BytesNumber + Offset; i++)
        {
            Data = InputData[i];

            
            for (j = 0; j < 8; j++)
            {
                if ((CrcCode & 128) >  0)
                {
                    CrcCode = (byte)(CrcCode * 2);

                    if ((Data & 128) > 0)
                    {
                        CrcCode++;
                    }
                    CrcCode = (byte)(CrcCode ^ Polinom);
                }
                else 
                {
                    CrcCode = (byte)(CrcCode * 2);
                    if ((Data & 128) > 0)
                    {
                        CrcCode++;
                    }
                }
                
                Data = (byte)(Data * 2);
            }  
        }
           
        return CrcCode;
    } 

В функцию передаю буфер байт без разделителей и байта контрольной суммы то есть: 3 204 151 15 15 16 0 0 0 0 0 0 0 0 0 0 0 0

У меня получается: 136

Товарищи, второй день сижу, что я делаю не так так?

killla
()
Ответ на: комментарий от James_Holden

эх молодежжжжжжжжжжж, опять весы

def Crc_8(n, poly, crc = 0):
    n = n ^ crc
    for bitnumber in range(0,8):
        if  n & 0x80:
            n = ( n << 1) ^ poly
        else:
            n = n << 1
    return n & 0xFF
  
message = [0xcc, 0xc3, 0x0f, 0x3f, 0x12, 0x6c, 0x26, 0xe0, 0x34, 0x12, 0x36, 0x72, 0x2e, 0xb1, 0x1c, 0xfa, 0x19]
crc = 0
for i in message:
    crc = Crc_8(n = i, poly = 0x69, crc = crc )
print(hex(crc))

написано как решение алгоритма в лоб, обычно делают на таблицах, если места много

только не говори что не знаешь как проверить контрошку :) я этого не переживу

Morin ★★★★
()
Ответ на: комментарий от Morin

Извини, а ты вообще о чем?

Вот в целом, на какой вопрос ты сейчас отвечал?

И почему ты решил, что я чего-то там не могу проверить? А?

James_Holden ★★★
() автор топика
Ответ на: комментарий от Morin

Погоди, тут интересная вещь.

Твой код правильно считает контрольную сумму на пакете @killla !

То есть то что мы в самом конце обсуждаем.

А «мой» код, считает неправильно, так же как и его код на C# выше. Но - правильно на пакетах от моих весов.

Твой код неправильно считает на моих пакетах.

Где-то то тут подвох.

Вот что у меня

uint8_t crc8(uint8_t input, uint8_t crc)
{
  for (int i=0; i<8; ++i) {
    int major = (crc & 0x80);
    crc <<= 1;
    if ((input & (1<<(7-i)))!=0)
      crc |= 1;
    if (major)
      crc ^= 0x69;
  }
  return crc;
}
James_Holden ★★★
() автор топика
Ответ на: комментарий от James_Holden

насколько помню у разных весов которые я видел контрошка иногда считается только с данных, но не с управляющих символов. хотя на мой взгляд это косяк проектирования

иными словами, откидываем старт стоп пакета, считаем контрошку всей посылки либо только данных, но не управляек

дай бог памяти, сейчас попытаюсь найти утилу которой ломал контрольку. либо сам погугли, там сразу увидишь что в выхлопе не нулевое смещение помимо полинома будет, это значит есть статичные данные

Morin ★★★★
()
Последнее исправление: Morin (всего исправлений: 3)
Ответ на: комментарий от James_Holden

3 204 146 16 1 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=101 | my_crc= 204

3 204 146 16 0 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=154 | my_crc= 9

3 204 146 16 255 254 16 0 0 0 0 0 0 0 0 0 0 0 PD_crc=0 | my_crc= 102

3 204 146 16 254 16 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=48 | my_crc= 143

3 204 146 16 254 16 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=48 | my_crc= 143

3 204 147 16 254 16 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=88 | my_crc= 169

3 204 148 16 255 254 16 0 0 0 0 0 0 0 0 0 0 0 PD_crc=0 | my_crc= 178

3 204 149 16 0 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=235 | my_crc= 251

3 204 149 16 2 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=124 | my_crc= 24

3 204 150 16 4 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=20 | my_crc= 62

3 204 150 16 6 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=131 | my_crc= 221

3 204 149 16 9 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=154 | my_crc= 9

3 204 148 16 11 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=101 | my_crc= 204

3 204 148 16 11 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=101 | my_crc= 204

3 204 147 16 12 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=59 | my_crc= 183

3 204 146 16 11 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=124 | my_crc= 24

3 204 146 16 10 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=131 | my_crc= 221

3 204 145 16 8 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=172 | my_crc= 84

3 204 145 16 6 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=242 | my_crc= 47

3 204 144 16 4 17 0 0 0 0 0 0 0 0 0 0 0 0 PD_crc=13 | my_crc= 234

killla
()
Ответ на: комментарий от Morin

Ббблиин, ты прав, я - идиот. Можешь начинать не переживать :)))

Проблема была в том, что в «мою» функцию надо подставлять данные + 0 вместо контрольной суммы. Тогда все совпадает. У себя я именно так считаю, а тут сегодня я взял пакет @killla и подставил без этого дополнительного байта.

А твоя функция считает без этого.

То есть, @killla, ты просто добавь в конец еще один 0 байт, и попробуй, все должно работать. Получается 70.

James_Holden ★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.