Прошу помочь разобраться с нестандартным протоколом, стандарт передачи RS485
- Войдите на сайт для отправки комментариев
Добрый день, форумчане.
В своем проекте использую плату ардуино 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 ).
Этот ответ посылается на запрос чтения данных из регулятора.
А что непонятно-то? Протокол описал - знай себе отправляй запросы, получай пакеты ответов, анализируй. Дело тривиальное. Примеров, как работать с RS-485 - валом, если в гугле не забанили.
Начал осваивать терминал, лед тронулся))
RS485 на первый взгляд прост, но на самом деле весьма коварен. Проблема в том, что приемники RS485 очень чувствительные, а бОльшую часть времени все передатчики выключены. При выключенных передатчиках на шину RS485 наводится всякая дрянь, отчего приемники начинают принимать ложные пакеты данных. А к моменту прихода настоящих данных уже находятся в каком-то "левом" состоянии и эти данные принять не могут.
Вот и в вашем случае, если помеха создаст на шине ложный символ * (ASCII код 0х42), то после этого все встанет раком и в нормальное состояние скорей всего вообще никогда не вернется, дальнейший обмен будет невозможен. По крайней мере я при беглом просмотре не заметил никаких средств для обработки этой ситуации.
Это типичная проблема всяких самодельных протоколов на основе RS485. Вот Modbus RTU сделан правильно, ему все нипочем. А всякие самопальные протоколы расчитаны только на то, что помеха никогда не пересилит резисторы подтяжки к 0 и +5. Увы, ребята, рано или поздно пересилит, рано или поздно ваша доморощенная система накроется.
Протокол пришел вместе с прибором, так что тут как говориться имеем то что имеем.
На данном этапе есть связь с прибором(пока что через терминал, использую Hterm), приходят пакеты, но возник вопрос чтения чесел в 24-битном формате. В протоколе есть функции чтения, но как их перевести в понятный ардуино формат, для этого не хватает знания языка программирования. Может кто-то помочь со скетчем, а именно переводом функций преобразования в понятный для ардуино язык?
Функции для чтения числа из регулятора.
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
}
Может кто-то помочь со скетчем, а именно переводом функций преобразования в понятный для ардуино язык?
Что такое "понятный_Ардуино_язык"? Кому он должен быть понятным, вам? Язык, который понимает Ардуино - это С/С++, это вам должно быть известно. Про какой еще язык вы спрашиваете?
Не могли бы вы задавать вопросы на обычном русском языке? Это означает, что если вы вводите свой жаргонный термин - некий "понятный_Ардуино_язык" - то дайте его определение общепринятыми словами.
В приложении к приведенным вами кускам текста это выглядит так. Тексты написаны на языке С, который Ардуино, несомненно, понимает. Однако в тексте встречаются термины, которые не являются стандартными. Hо в отличие от вашего невесть что означающего "понятного_Ардуино_языка", содержание этих терминов как раз понятно и их можно определить самому:
#define BYTE unsigned char
#define WORD unsigned int
и т.д. пока компилятор не перестанет ругаться.
На данном этапе есть связь с прибором(пока что через терминал, использую Hterm), приходят пакеты, но возник вопрос чтения чесел в 24-битном формате.
Вообще-то у 8-разрядных версий Ардуино есть родной 24-разрядный формат: __int24. Но, кроме того, если речь идет о последовательном вводе данных - то тут вообще размер данных не играет роли - можно побайтово читать 24-разрядные денные в 32-разрядные переменные.