Прошу помочь разобраться с нестандартным протоколом, стандарт передачи RS485

romz_ru
Offline
Зарегистрирован: 24.05.2018

Добрый день, форумчане. 

В своем проекте использую плату ардуино MEGA 256.

Задача стоит по сбору данных с приборов и датчиков(пирометр, варуумметр, термодатчики), а так же управление работой нагревателя(ПИД-регулирование).

Подружиться с пирометром, организовать ПИД-регулирование - это получилось сделать, все прекрасно работает, но есть проблема со связью платы ардуино и вакуумметра, так как в отличии от пирометра используется нестандартный протокол связи, а так как на данном этапе знаний по программированию у меня недостаточно то прошу специалистов помочь разобраться как перевести нестандартный протокол в понятный для ардуино формат и рассказать/показать как это будет выглядеть в самом скетче.

Для информации:

контроллер ардуино собирает данные и передает в  СКАД-у(использую SimplSCADA).

Вакуумметр РВЭ-4.3, может кто с таким сталкивался и уже решал подобную задачу.

Стандарт передачи данных - RS485.

Ниже привожу целиком без урезания описание протокола(был предоставлен разработчиком вакуумметра):

 

Запрос к регулятору –

* AA CL <aaa><dd…> CS CR

*          - символ начала посылки

AA      - две шестнадцатеричные цифры – сетевой адрес регулятора ( параметр настройки    Р22, см. «Руководство по эксплуатации» ).

С         - шестнадцатеричная цифра – команда – определяет смысловое содержание запроса.

            00XY ( биты полубайта )

            00        - Зарезервировано ( должны быть установлены в 0 ).

            X         - 0 – Команда / 1 – Данные.

            Y         - 0 – Чтение / 1 – Запись.

L         - шестнадцатеричная цифра – длина области данных в байтах.

ааа       - три шестнадцатеричные цифры – адрес памяти регулятора для данной операции.

dd       - область данных ( длина определяется параметром L ).

CS       - контрольная сумма.

CR      - символ возврат каретки ( 0х0D ).

В запросе может отсутствовать адрес памяти регулятора и область данных.

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

Функция для расчета контрольной суммы на языке С++.

BYTE CalculateCRC( CString Str ) {

            BYTE CRC = 0;

            for ( int i = 0; i < Str.GetLength(); i++ ) CRC += (BYTE)(Str[i]);

            CRC = ~CRC + 1;

            return CRC;

}

Команды :

01 – пуск работы регулятора.

02 – стоп работы регулятора.

03 – получение статуса регулятора.

 

Пример команды

«*021101B1<CR>» - пуск регулятора с сетевым адресом 0х02

«*021102B0<CR>» - стоп регулятора с сетевым адресом 0х02

«*020311<CR>» - получение статуса регулятора с сетевым адресом 0х02

 

Запись данных в регулятор

«* 02 33 0 5A 01 02 03 42<CR>» - запись в память регулятора с сетевым адресом 0х02 значений 0x01, 0x02, 0x03 начиная с адреса 0х05А.

 

Чтение данных из регулятора

«* 02 23 0 5A 69 <CR>» - чтение из памяти регулятора с сетевым адресом 0х02 трех бай

 данных начиная с адреса 0х05А.

 

Все данные в регуляторе предоставлены в формате чисел с плавающей запятой согласно стандарта IEEE 754 с усеченной до 16 бит мантиссой, т.е. в 24 битном формате.

Ниже предоставлены функции для преобразования чисел типа float ( 32-битное число с плавающей запятой в стандарте языка С++ ) в усеченный 24 битный формат IEEE 754.

 

Функции для записи числа в регулятор.

float float_to_PIC24( float Num ) {

            float Numb = Num/4.F;

            BYTE *NumBytes = (BYTE*) &Numb;

            float Result;

            BYTE *ResBytes = (BYTE*) &Result;

 

            if ( Num == 0.0F ) return Num;

 

            ResBytes[0] = 0;

            ResBytes[1] = NumBytes[1];

            ResBytes[2] = NumBytes[2];

            ResBytes[3] = ( NumBytes[3] << 1 ) + 2;

            if ( NumBytes[2] & 0x80 )

                        ResBytes[3] |= 1;

            else

                        ResBytes[3] &= 0xFE;

            if ( NumBytes[3] & 0x80 )

                        ResBytes[2] |= 0x80;

            else

                        ResBytes[2] &= 0x7F;

            return Result;

}

 

void put_PIC24_float( BYTE *Num, float RVar ) {

            BYTE *RBVar = (BYTE*) &RVar;

            Num[0] = RBVar[2];

            Num[1] = RBVar[1];

            Num[2] = RBVar[3];

}

 

int WriteRTE54Float( BYTE NetAdr, WORD Adr, float Value ) {

            CString SendStr;

            CString RecvStr;

            SendStr.Format( "*%02X33%03X", NetAdr, Adr );

            BYTE byteValue[3];

            put_PIC24_float( byteValue, float_to_PIC24( Value ) );

            for ( int i = 0; i < 3; i++ ) SendStr += Byte2Hex( byteValue[i] );

SendStr += CalculateCRC(SendStr );

SendStr += _CR;

            return ProcessRS485Cmd( SendStr, RecvStr );

}

 

Функции для чтения числа из регулятора.

 

BYTE Hex2Byte( CString Str ) {

            BYTE retVar = Char2Byte( Str[0] )*16 + Char2Byte( Str[1] );

            return retVar;

}

 

float get_PIC24_float( BYTE *Num ) {

            float RVar;

            BYTE *RBVar = (BYTE*) &RVar;

            RBVar[0] = 0;

            RBVar[1] = Num[1];

            RBVar[2] = Num[0];

            RBVar[3] = Num[2];

            return RVar;

}

 

float PIC24_to_float( float Num ) {

            BYTE *NumBytes = (BYTE*) &Num;

            float Result;

            BYTE *ResBytes = (BYTE*) &Result;

            if ( Num == 0.0F ) return 0.0F;

            ResBytes[0] = 0;

            ResBytes[1] = NumBytes[1];

            ResBytes[2] = NumBytes[2];

            ResBytes[3] = ( NumBytes[3] - 2 ) >> 1;

            if ( NumBytes[3] & 1 )

                        ResBytes[2] |= 0x80;

            else

                        ResBytes[2] &= 0x7F;

            if ( NumBytes[2] & 0x80 )

                        ResBytes[3] |= 0x80;

            else

                        ResBytes[3] &= 0x7F;

            return Result*4.F;

 

}

 

int LoadRTE54Float( BYTE NetAdr, WORD Adr, float& Value ) {

            CString RecvStr = "";

            CString SendStr;

            SendStr.Format( "*%02X23%03X", NetAdr, Adr );

SendStr += CalculateCRC(SendStr );

SendStr += _CR;

            if ( ProcessRS485Cmd( SendStr, RecvStr )  ) {

                        if ( RecvStr == "" ) {

                                   Value = 0.0f;

                        }

                        else {

                                   BYTE FlContents[3];

                                   FlContents[0] = Hex2Byte( RecvStr.Mid( 3 ) );

                                   FlContents[1] = Hex2Byte( RecvStr.Mid( 5 ) );

                                   FlContents[2] = Hex2Byte( RecvStr.Mid( 7 ) );

                                   float flValue = get_PIC24_float( FlContents );

                                   Value = PIC24_to_float( flValue );

                        }

                        return TRUE;

            }

            return FALSE; // Invalid Device Answer

}

 

Адреса для чтения – записи параметров регулятора.

 

Адрес текущего значения температуры – 0х0А0.

 

Параметры в регуляторе нумеруются как P XY (см. «Руководство по эксплуатации»).

Адрес параметра в памяти вычисляется следующим образом :

            Адрес = 0x800 + X*3 + Y*9;

 

Программа регулятора находится в памяти, начиная с адреса 0х84B.

Порядок расположения параметров программы

Pt[0], Pu[0], PC[0], Pt[1], Pu[1], PC[1],…. Pt[15], Pu[15], PC[15]

 

 

Регулятор отвечает на запросы и команды следующими посылками

 

Подтверждение

!AACR

AA      - две шестнадцатеричные цифры – сетевой адрес регулятора ( параметр настройки    Р22, см. «Руководство по эксплуатации» ).

CR      - символ возврат каретки ( 0х0D ).

 

Этот ответ посылается в случае успешного выполнения операции пуска, останова и записи данных в регулятор.

 

Ошибка

?AACR

AA      - две шестнадцатеричные цифры – сетевой адрес регулятора ( параметр настройки    Р22, см. «Руководство по эксплуатации» ).

CR      - символ возврат каретки ( 0х0D ).

 

 

Ответ с данными

!AAdd…CSCR

AA      - две шестнадцатеричные цифры – сетевой адрес регулятора ( параметр настройки    Р22, см. «Руководство по эксплуатации» ).

dd       - область данных ( длина определяется параметром L запроса ).

CS       - контрольная сумма.

CR      - символ возврат каретки ( 0х0D ).

 

Этот ответ посылается на запрос чтения данных из регулятора.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

А что непонятно-то? Протокол описал - знай себе отправляй запросы, получай пакеты ответов, анализируй. Дело тривиальное. Примеров, как работать с RS-485 - валом, если в гугле не забанили.

romz_ru
Offline
Зарегистрирован: 24.05.2018

Начал осваивать терминал, лед тронулся))

 

triac
triac аватар
Offline
Зарегистрирован: 03.05.2018

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

Вот и в вашем случае, если помеха создаст на шине ложный символ * (ASCII код 0х42), то после этого все встанет раком и в нормальное состояние скорей всего вообще никогда не вернется, дальнейший обмен будет невозможен. По крайней мере я при беглом просмотре не заметил никаких средств для обработки этой ситуации.

Это типичная проблема всяких самодельных протоколов на основе RS485. Вот Modbus RTU сделан правильно, ему все нипочем. А всякие самопальные протоколы расчитаны только на то, что помеха никогда не пересилит резисторы подтяжки к 0 и +5. Увы, ребята, рано или поздно пересилит, рано или поздно ваша доморощенная система накроется.

romz_ru
Offline
Зарегистрирован: 24.05.2018

Протокол пришел вместе с прибором, так что тут как говориться имеем то что имеем. 

На данном этапе есть связь с прибором(пока что через терминал, использую Hterm), приходят пакеты, но возник вопрос чтения чесел в 24-битном формате. В протоколе есть функции чтения, но как их перевести в понятный ардуино формат, для этого не хватает знания языка программирования. Может кто-то помочь со скетчем, а именно переводом функций преобразования в понятный для ардуино язык?

 

romz_ru
Offline
Зарегистрирован: 24.05.2018

Функции для чтения числа из регулятора.

BYTE Hex2Byte( CString Str ) {

            BYTE retVar = Char2Byte( Str[0] )*16 + Char2Byte( Str[1] );

            return retVar;

}

float get_PIC24_float( BYTE *Num ) {

            float RVar;

            BYTE *RBVar = (BYTE*) &RVar;

            RBVar[0] = 0;

            RBVar[1] = Num[1];

            RBVar[2] = Num[0];

            RBVar[3] = Num[2];

            return RVar;

}

float PIC24_to_float( float Num ) {

            BYTE *NumBytes = (BYTE*) &Num;

            float Result;

            BYTE *ResBytes = (BYTE*) &Result;

            if ( Num == 0.0F ) return 0.0F;

            ResBytes[0] = 0;

            ResBytes[1] = NumBytes[1];

            ResBytes[2] = NumBytes[2];

            ResBytes[3] = ( NumBytes[3] - 2 ) >> 1;

            if ( NumBytes[3] & 1 )

                        ResBytes[2] |= 0x80;

            else

                        ResBytes[2] &= 0x7F;

            if ( NumBytes[2] & 0x80 )

                        ResBytes[3] |= 0x80;

            else

                        ResBytes[3] &= 0x7F;

            return Result*4.F;

}

int LoadRTE54Float( BYTE NetAdr, WORD Adr, float& Value ) {

            CString RecvStr = "";

            CString SendStr;

            SendStr.Format( "*%02X23%03X", NetAdr, Adr );

SendStr += CalculateCRC(SendStr );

SendStr += _CR;

            if ( ProcessRS485Cmd( SendStr, RecvStr )  ) {

                        if ( RecvStr == "" ) {

                                   Value = 0.0f;

                        }

                        else {

                                   BYTE FlContents[3];

                                   FlContents[0] = Hex2Byte( RecvStr.Mid( 3 ) );

                                   FlContents[1] = Hex2Byte( RecvStr.Mid( 5 ) );

                                   FlContents[2] = Hex2Byte( RecvStr.Mid( 7 ) );

                                   float flValue = get_PIC24_float( FlContents );

                                   Value = PIC24_to_float( flValue );

                        }

                        return TRUE;

            }

            return FALSE; // Invalid Device Answer

}

triac
triac аватар
Offline
Зарегистрирован: 03.05.2018

romz_ru пишет:

 Может кто-то помочь со скетчем, а именно переводом функций преобразования в понятный для ардуино язык?

Что такое "понятный_Ардуино_язык"? Кому он должен быть понятным, вам? Язык, который понимает Ардуино - это С/С++, это вам должно быть известно. Про какой еще язык вы спрашиваете?

Не могли бы вы задавать вопросы на обычном русском языке? Это означает, что если вы вводите свой жаргонный термин - некий "понятный_Ардуино_язык" - то дайте его определение общепринятыми словами.

В приложении к приведенным вами кускам текста это выглядит так. Тексты написаны на языке С, который Ардуино, несомненно, понимает. Однако в тексте встречаются термины, которые не являются стандартными. Hо в отличие от вашего невесть что означающего "понятного_Ардуино_языка", содержание этих терминов как раз понятно и их можно определить самому:

#define BYTE unsigned char

#define WORD unsigned int

и т.д. пока компилятор не перестанет ругаться.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

romz_ru пишет:

На данном этапе есть связь с прибором(пока что через терминал, использую Hterm), приходят пакеты, но возник вопрос чтения чесел в 24-битном формате.

Вообще-то у 8-разрядных версий Ардуино есть родной 24-разрядный формат: __int24. Но, кроме того, если речь идет о последовательном вводе данных - то тут вообще размер данных не играет роли - можно побайтово читать 24-разрядные денные в 32-разрядные переменные.