Что такое modbus со строны программирования?

JonHappy1
Offline
Зарегистрирован: 11.06.2018

есть библиотеки. но если нет необходимости использовать все функции этих либ, можно ли заменить на элементарное

byte data[8];
Serial.write(data,8)

предварительно заполнить data данными и crc ?

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Можно, а чего нельзя-то? Вот с поглода назад Вам бы это запретили, а сейчас всё можно - время такое :(

JonHappy1
Offline
Зарегистрирован: 11.06.2018

ЕвгенийП пишет:

Можно, а чего нельзя-то? Вот с поглода назад Вам бы это запретили, а сейчас всё можно - время такое :(

а почему запретили?

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Это был сарказм.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

JonHappy1 пишет:
а почему запретили?

Поищите по форуму фразу "я запретил" - узнаете много интересного.

JonHappy1
Offline
Зарегистрирован: 11.06.2018

ЕвгенийП пишет:

JonHappy1 пишет:
а почему запретили?

Поищите по форуму фразу "я запретил" - узнаете много интересного.

это флуд.
а по делу, есть что-то дельное?

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

Первый ответ самый дельный

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

JonHappy1 пишет:

ЕвгенийП пишет:

JonHappy1 пишет:
а почему запретили?

Поищите по форуму фразу "я запретил" - узнаете много интересного.

это флуд.
а по делу, есть что-то дельное?

Это не флуд, а ответ на Ваш вопрос.

По делу я Вам полностью ответил в посте №1 (что ещё можно добавить?), о чём начинаю сожалеть, т.к. сложилось ощущение, что Вы считаете меня чем-то Вам задолжавшим.

 

JonHappy1
Offline
Зарегистрирован: 11.06.2018

ЕвгенийП пишет:

JonHappy1 пишет:

ЕвгенийП пишет:

JonHappy1 пишет:
а почему запретили?

Поищите по форуму фразу "я запретил" - узнаете много интересного.

это флуд.
а по делу, есть что-то дельное?

Это не флуд, а ответ на Ваш вопрос.

По делу я Вам полностью ответил в посте №1 (что ещё можно добавить?), о чём начинаю сожалеть, т.к. сложилось ощущение, что Вы считаете меня чем-то Вам задолжавшим.

 


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

Mikhail72
Offline
Зарегистрирован: 22.06.2018

Всех приветствую, вопрос из этой же темы, поэтому новую создавать не стал.

Есть устройство которое через UART посылает данные, которые заканчиваются CRC16 Modbus. Я принимаю эти данные например 4 байта, два из которых CRC, но немогу пощитать CRC чтобы сравнить верный ли пакет. Пакет в HEX выглядит так 56 00 E3 10.

Использую функцию форуме Arduino.cc

/*
 From: http://www.ccontrolsys.com/w/How_to_Compute_the_Modbus_RTU_Message_CRC

Using the 8 character ASCII input DEADBEEF (upper case)
the checksum is 0xDD18
The code below agrees with the online calculator here:
http://www.lammertbies.nl/comm/info/crc-calculation.html

*/

#define UInt16 uint16_t
// CRC should be DD18
char *t = (char *)"DEADBEEF";
// Compute the MODBUS RTU CRC
UInt16 ModRTU_CRC(char * buf, int len)
{
  UInt16 crc = 0xFFFF;
 
  for (int pos = 0; pos < len; pos++) {
    crc ^= (UInt16)buf[pos];          // XOR byte into least sig. byte of crc
 
    for (int i = 8; i != 0; i--) {    // Loop over each bit
      if ((crc & 0x0001) != 0) {      // If the LSB is set
        crc >>= 1;                    // Shift right and XOR 0xA001
        crc ^= 0xA001;
      }
      else                            // Else LSB is not set
        crc >>= 1;                    // Just shift right
    }
  }
  // Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
  return crc;  
}

void setup()
{
  Serial.begin(9600);
  while(!Serial);
  delay(1000);
  
  Serial.println(ModRTU_CRC(t,strlen(t)),HEX);
}

void loop()
{

}

Дописал для своего теста

#define UInt16 uint16_t

int countByte = 0;
char val[]="";

UInt16 ModRTU_CRC(char buf[], int len)
{
  UInt16 crc = 0xFFFF;
 
  for (int pos = 0; pos < len; pos++) {
    crc ^= (UInt16)buf[pos];          // XOR byte into least sig. byte of crc
 
    for (int i = 8; i != 0; i--) {    // Loop over each bit
      if ((crc & 0x0001) != 0) {      // If the LSB is set
        crc >>= 1;                    // Shift right and XOR 0xA001
        crc ^= 0xA001;
      }
      else                            // Else LSB is not set
        crc >>= 1;                    // Just shift right
    }
  }
  // Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
  return crc;  
}

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  while(!Serial);
  delay(1000);
}

void loop()
{
countByte = Serial1.available();
  Serial.print("countByte: ");
  Serial.println(countByte);
  if(Serial1.available() > 0) {
  for(int i=0;i<=countByte;i++){
    val[i] = Serial1.read();
  Serial.println(val[i],HEX);
  }
  }
Serial.println(ModRTU_CRC(val,strlen(val)),HEX);
delay(5000);
}

При получении указанных выше данных, программа считает CRC только для первого байта V(ASCII) или 56(HEX). Думаю причина во втором байте, который NULL или 0x00. 

Помогите, пожалуйста, а то знаний в этом вопросе нехватает.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Резервируйте память для char val[] в размере длины пакета, сейчас вы пишете неизвестно в какую область памяти. strlen тут использовать неверно.

Mikhail72
Offline
Зарегистрирован: 22.06.2018

Пакеты могут быть разной длинны, 4 байта самый короткий, самый длинный если не ошибаюсь 20 байт. Ну если strlen неверно тогда, жду предложений.

Есть ещё один код из сети, но там данные тоже String msg = "5600E310"; //like Modbus menssage: 56 00 E3 10 Тоже не могу понять как привети полученные данные к нужному виду.

JonHappy1
Offline
Зарегистрирован: 11.06.2018

получить указатель на строку и преобразовать в массив читая по два симвала до символа с кодом 0x00

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Mikhail72 пишет:

Пакеты могут быть разной длинны, 4 байта самый короткий, самый длинный если не ошибаюсь 20 байт. Ну если strlen неверно тогда, жду предложений.

Значит резервируйте 20 байт. В функцию расчета передавайте длину принятого пакета за вычетом размера CRC (вы же не собираетесь CRC от CRC считать?)

Mikhail72
Offline
Зарегистрирован: 22.06.2018

JonHappy1 пишет:
получить указатель на строку и преобразовать в массив читая по два симвала до символа с кодом 0x00

Я или неправильно объяснил или не понимаю написанного, но вопрос в том чтобы преобразовать полученные из порта данные в строку типа "5600Е310"

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

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

А если знал, зачем спрашивал?

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

Ещё раз перечитал первый пост. Нет там ничего ни про какие камни.

JonHappy1 пишет:
вот и вопрос.

Где? Вопроса, кроме как "можно ли?" так и не поступило.

Вы уж, постарайтесь спрашивать именно то, что Вас интересует. А то вон спросили про запреты, Вам ответили, а Вы про флуд раскричались, спросили про "можно ли", Вам ответили - опять не слава Богу.

В чём же Ваш вопрос-то?

 

b707
Онлайн
Зарегистрирован: 26.05.2017

Mikhail72 пишет:

Я или неправильно объяснил или не понимаю написанного, но вопрос в том чтобы преобразовать полученные из порта данные в строку типа "5600Е310"

то есть  у вас вопрос не о расчете CRC. а о том, как собрать полученные байты в строку. В принципе sadman вам уже ответил, но если не понимаете = ищите выделенное в гугле - тема новичковая, на форумах разобрана тысячу раз.

mobistrike
mobistrike аватар
Онлайн
Зарегистрирован: 19.08.2016

ЕвгенийП пишет:

JonHappy1 пишет:
а почему запретили?

Поищите по форуму фразу "я запретил" - узнаете много интересного.

Сорри за оффтоп , а куда делся главный "запрещатель" )) ?

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

mobistrike пишет:

Сорри за оффтоп , а куда делся главный "запрещатель" )) ?

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

b707
Онлайн
Зарегистрирован: 26.05.2017

DetSimen пишет:

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

раньше его это не останавливало - цифирьки менял в нике и воскресал. Так что тут, скорее, другое  - в отпуск уехал или комп сломался :).. Думаю, мы его еще увидим :)

Mikhail72
Offline
Зарегистрирован: 22.06.2018

b707 пишет:

Mikhail72 пишет:

Я или неправильно объяснил или не понимаю написанного, но вопрос в том чтобы преобразовать полученные из порта данные в строку типа "5600Е310"

то есть  у вас вопрос не о расчете CRC. а о том, как собрать полученные байты в строку. В принципе sadman вам уже ответил, но если не понимаете = ищите выделенное в гугле - тема новичковая, на форумах разобрана тысячу раз.

Я так понял Вы имеете ввиду это и судя по той теме если у меня есть массив byte msg[5]={0x48,0x65,0x6C,0x6C,0x6F}, то после преобразования я получу строку "Hello", а мне нужно "48656C6C6F"

Logik
Offline
Зарегистрирован: 05.08.2014

b707 пишет:

DetSimen пишет:

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

раньше его это не останавливало - цифирьки менял в нике и воскресал. Так что тут, скорее, другое  - в отпуск уехал или комп сломался :).. Думаю, мы его еще увидим :)

Просто его либка по работе с кнопкой перестала умещатся в ардуино. Вот и утратил интерес к платформе, теперь на форуме стм околачивается ;)

sadman41
Онлайн
Зарегистрирован: 19.10.2016

А это имеет смысл? Возможно, что библиотека имеет другую функцию, которая прямо байтовый массив жрет и можно обойтись без HEX-представления.

b707
Онлайн
Зарегистрирован: 26.05.2017

Mikhail72 пишет:

Я так понял Вы имеете ввиду это и судя по той теме если у меня есть массив byte msg[5]={0x48,0x65,0x6C,0x6C,0x6F}, то после преобразования я получу строку "Hello", а мне нужно "48656C6C6F"

вы пишете ерунду. Для расчета CRC вам нужен как раз массив byte msg[5]={0x48,0x65,0x6C,0x6C,0x6F},, а не строка "48656C6C6F". Ничего преобразовывать не нужно.

b707
Онлайн
Зарегистрирован: 26.05.2017

sadman41 пишет:

А это имеет смысл? Возможно, что библиотека имеет другую функцию, которая прямо байтовый массив жрет и можно обойтись без HEX-представления.

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

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

Mikhail72
Offline
Зарегистрирован: 22.06.2018

b707 пишет:

Mikhail72 пишет:

Я так понял Вы имеете ввиду это и судя по той теме если у меня есть массив byte msg[5]={0x48,0x65,0x6C,0x6C,0x6F}, то после преобразования я получу строку "Hello", а мне нужно "48656C6C6F"

вы пишете ерунду. Для расчета CRC вам нужен как раз массив byte msg[5]={0x48,0x65,0x6C,0x6C,0x6F},, а не строка "48656C6C6F". Ничего преобразовывать не нужно.

Есть ещё вот такой вариант кода, поэтому прошу не выражаться.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(19200);
}

void loop() {
  // put your main code here, to run repeatedly:
  String raw_msg = "01030000000AC5CD"; //like Modbus menssage: 01 03 00 00 00 0A C5 CD
  String raw_msg_data = raw_msg.substring(0, raw_msg.length()-4); //Modbus message without crc code
  String raw_msg_crc = raw_msg.substring(raw_msg.length() - 4, raw_msg.length()); //only Modbus message crc code
  String calculated_crc = "";

  Serial.println("Modbus message: " + raw_msg);
  Serial.println("Modbus data: " + raw_msg_data);
  Serial.println("Modbus CRC: " + raw_msg_crc);
  
  calculated_crc = ModRTU_CRC(raw_msg_data); //Modbus message without crc code. Reply the calculated crc code
  Serial.println("---------------------------------");
  Serial.println("Modbus CRC Calculated: " + calculated_crc);
  while(1);
}

// Compute the MODBUS RTU CRC
String ModRTU_CRC(String raw_msg_data) {
  //Calc raw_msg_data length
  byte raw_msg_data_byte[raw_msg_data.length()/2];
  //Convert the raw_msg_data to a byte array raw_msg_data
  for (int i = 0; i < raw_msg_data.length() / 2; i++) {
    raw_msg_data_byte[i] = StrtoByte(raw_msg_data.substring(2 * i, 2 * i + 2));
  }

  //Calc the raw_msg_data_byte CRC code
  uint16_t crc = 0xFFFF;
  String crc_string = "";
  for (int pos = 0; pos < raw_msg_data.length()/2; pos++) {
    crc ^= (uint16_t)raw_msg_data_byte[pos];          // XOR byte into least sig. byte of crc
    for (int i = 8; i != 0; i--) {    // Loop over each bit
      if ((crc & 0x0001) != 0) {      // If the LSB is set
        crc >>= 1;                    // Shift right and XOR 0xA001
        crc ^= 0xA001;
      }
      else                            // Else LSB is not set
        crc >>= 1;                    // Just shift right
    }
  }
  // Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)

  //Become crc byte to a capital letter String 
  crc_string = String(crc, HEX);
  crc_string.toUpperCase();
  
  //The crc should be like XXYY. Add zeros if need it
  if(crc_string.length() == 1){
    crc_string = "000" + crc_string;
  }else if(crc_string.length() == 2){
    crc_string = "00" + crc_string;
  }else if(crc_string.length() == 3){
    crc_string = "0" + crc_string;
  }else{
    //OK
  }

  //Invert the byte positions
  crc_string = crc_string.substring(2, 4) + crc_string.substring(0, 2);
  return crc_string;  
}

//String to byte --> Example: String = "C4" --> byte = {0xC4}
byte StrtoByte (String str_value){
  char char_buff[3];
  str_value.toCharArray(char_buff, 3);
  byte byte_value = strtoul(char_buff, NULL, 16);
  return byte_value;  
}

 

b707
Онлайн
Зарегистрирован: 26.05.2017

Mikhail72 пишет:

Есть ещё вот такой вариант кода, поэтому прошу не выражаться.

да как же тут не выражаться, если в этом коде для расчета CRC автор преобразует строки типа "4665ff34e4" как раз в массив байт (строчки 28-30)

У вас уже есть этот массив байт - и вы предлагаете сначала перевести его в строку, а потом этим кодом - обратно? Ну не ерунда ли?

Mikhail72
Offline
Зарегистрирован: 22.06.2018

b707 пишет:

Mikhail72 пишет:

Есть ещё вот такой вариант кода, поэтому прошу не выражаться.

да как же тут не выражаться, если в этом коде для расчета CRC автор преобразует строки типа "4665ff34e4" как раз в массив байт (строчки 28-30)

У вас уже есть этот массив байт - и вы предлагаете сначала перевести его в строку, а потом этим кодом - обратно? Ну не ерунда ли?

Если у Вас есть вариант перевода предлагайте может кому пригодиться, т.к. автор кода его где-то приминяет, а иначе это вопрос снят.

По поводу первого кода, да там strlen() всегда выдавала 1, поэтому CRC была только по первому байту. Сейчас тестовый код работает, только для полного счастья еще нужно посчитать количество байт в массиве, сейчас запись имеет вид

Serial.println(ModRTU_CRC((val),countByte-2),HEX);

А хотелось бы

Serial.println(ModRTU_CRC((val),sizeof(val)-2),HEX);

но т.к. массив зарезервирован как

byte val[20];

То sizeof(val) возвращает 20.

Поэтому изучаю дальше.

 

b707
Онлайн
Зарегистрирован: 26.05.2017

Mikhail72 пишет:

для полного счастья еще нужно посчитать количество байт в массиве, сейчас запись имеет вид

Serial.println(ModRTU_CRC((val),countByte-2),HEX);

А хотелось бы

Serial.println(ModRTU_CRC((val),sizeof(val)-2),HEX);

мало ли что кому хотелось.

Конкретно можете сказать, чем вас не устраивает первая запись?

Mikhail72
Offline
Зарегистрирован: 22.06.2018

Если рассматривать приём данных, то там всё вроде пока в порядке. Но есть ещё и отправка данных, можно конечно создать нужное количество массивов и посылать взависимости от команды, но думаю у 328P может не хватить объема памяти на всё, что планируется в нее загрузить. Правильнее было бы сформировать массив внутри программы и отправить

Serial.write(msg,sizeof(msg));

Но если массив имеет зарезервированную днинну, то sizeof() её и возвращает.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

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

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Mikhail72 пишет:

Но если массив имеет зарезервированную днинну, то sizeof() её и возвращает.

Как не возвращает? Я думал, она "в хорошем смысле".

b707
Онлайн
Зарегистрирован: 26.05.2017

Mikhail72 пишет:

Правильнее было бы сформировать массив внутри программы и отправить

Serial.write(msg,sizeof(msg));

Похоже, что вы не понимаете элементарных вещей. Зачем под каждую длину данных отводить свой массив? Пользуйтесь одним и тем же. Да, его длина не будет совпадать с размером данных и sizeOF() вы использовать не сможете. Ну и что с того?

Вы разве в момеент отправки не знаете длину данных? А если знаете, что мешает вместо sizeof() указать реальную длину?

 

nik182
Онлайн
Зарегистрирован: 04.05.2015

Заменить можно, при условии сохранения протокола. Просто выдать строку недостаточно. Нужно ещё соблюсти тайминги между посылками. А так, без библиотек слейвы работают замечательно.

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

sadman41 пишет:
вы же не собираетесь CRC от CRC считать?
Открою тебе страшную тайну, контрольная сумма строки вместе с CRC равна нулю.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Andy пишет:

sadman41 пишет:
вы же не собираетесь CRC от CRC считать?
Открою тебе страшную тайну, контрольная сумма строки вместе с CRC равна нулю.

Раскрытие этой тайны придает смысл расчету CRC по строке уже содержащей CRC?

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

sadman41 пишет:
Раскрытие этой тайны придает смысл расчету CRC по строке уже содержащей CRC?
Не для ламеров.

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

Andy пишет:

Открою тебе страшную тайну, контрольная сумма строки вместе с CRC равна нулю.

Пруф в студию.