Использование библиотеки ArduinoModbus для чтения регистра хранения со значением -1

Функция

client.read() в библиотеке ArduinoModbus возвращает -1 при возникновении ошибки: https://www.arduino.cc/reference/en/libraries/arduinomodbus/client.read/. Как её использовать для чтения регистра хранения со значением -1?

Одним из вариантов было бы отслеживать client.lastError(), но, похоже, нет способа сбросить lastError, поэтому это будет работать только до первой ошибки.


Я предполагаю, что для регистра со значением знакового типа, например int16_t, необходимо извлечь младшие 16 бит из значения uint32_t, возвращаемого функцией client.read().

, 👍0

Обсуждение

Разве Modus не передаёт только байты? ... у них нет знака, @jsotola

@jsotola Регистры Modbus могут иметь отрицательные значения, поэтому знак имеет значение., @Paul Jurczak

Я предполагаю, что данные будут считываться как беззнаковое целое число... данные могут занимать 2 байта, а значение, возвращаемое библиотекой, — 4 байта... верхние два байта используются для того, чтобы сделать число отрицательным., @jsotola

@jsotola Да, думаю, так. Я как раз добавлял комментарий к своему посту по этому поводу, а вы уже отправили свой., @Paul Jurczak

Вот пример того, как это работает... Первая строка будет сообщением об ошибке... Второе 32-битное значение не является отрицательным, но 16-битные данные отрицательны... https://wokwi.com/projects/403163467946550273, @jsotola

Существуют две ошибки, возвращающие -1. Для библиотеки Modbus Arduino ошибка клиента (-1) означает «Ошибка ввода-вывода». Для протокола Modbus исключение 1 означает «Недопустимый код функции». Вызов client.lastError() вернёт строковую интерпретацию кода ошибки. Пожалуйста, предоставьте свой код и то, что вы пытаетесь сбросить/установить через Modbus. Вы используете протокол TCP или RTU?, @hcheung

Как передать int16_t в качестве кода функции Modbus? Почему для кода функции Modbus нужно использовать int16_t вместо uint16_t или двух байт uint8_t? Знаете ли вы, что Modbus использует Big Endian при передаче, в то время как данные, хранящиеся в Arduino, — в Small Endian?, @hcheung

@hcheung Я разместил свой код в качестве ответа., @Paul Jurczak


2 ответа


2

Согласно описанию в Википедии данных Modbus модель, «входные регистры» и «регистры хранения» содержат 16-битные Беззнаковые числа в диапазоне 0–65 535 (0x0000–0xffff). Это в соответствии с прототипом ArduinoModbus client.write(), который принимает параметр unsigned int.

Функция client.read(), с другой стороны, возвращает long. Обычно это 32-битное число, которое может содержать все возможные значения допустимые значения, а также −1 для обозначения состояния ошибки.

Если, в отличие от модели данных Modbus, ваше устройство Modbus передает знаковые значения, вам следует проверить его техническое описание, чтобы увидеть, как отрицательные значения закодированы. Скорее всего, будет использоваться дополнительный код Представление, которое используется внутри Arduino. Если это в этом случае то, что устройство хранит как −1, будет прочитано библиотекой как 65 535. Вы можете легко получить правильное число, преобразовав его в int16_t:

long result = client.read();  // используйте `long`, чтобы не потерять информацию
if (result == -1) {
    handle_the_error();
    return;
}
int16_t value = result;  // преобразовать в тип устройства
if (value == -1) {
    handle_a_legit_minus_one();
}
,

0

Вот мой код для решения этой проблемы и дополнительного отделения кода ошибки от считанного значения. Модель данных Modbus, упомянутая в другом ответе, недостаточна для современного оборудования, что вынудило многих производителей выйти за её рамки и использовать регистры int32_t, uint32_t и float.

template<typename T>
bool holdingRegisterWrite16(ModbusTCPClient &modbus, int addr, T arg) {
  return modbus.holdingRegisterWrite(addr, arg);
}


template<typename T>
bool holdingRegisterRead16(ModbusTCPClient &modbus, int addr, T &arg) {
  static_assert(sizeof(T) == 2, "arg has to be a 16-bit type");

  auto res = modbus.holdingRegisterRead(addr);

  if (res == -1)
    return false;

  memcpy(&arg, &res, 2);
  return true;
}


template<typename T>
bool holdingRegisterWrite32(ModbusTCPClient &modbus, int addr, T arg) {
  uint16_t word0, word1;

  memcpy(&word0, &arg, 2);
  memcpy(&word1, (char*)&arg+2, 2);

  return 
    modbus.beginTransmission(HOLDING_REGISTERS, addr, 2)  && 
    modbus.write(word0)  &&  modbus.write(word1)  &&  modbus.endTransmission();
}


template<typename T>
bool holdingRegisterRead32(ModbusTCPClient &modbus, int addr, T &arg) {
  static_assert(sizeof(T) == 4, "arg has to be a 32-bit type");

  if (modbus.requestFrom(HOLDING_REGISTERS, addr, 2) != 2) 
    return false;

  auto res0 = modbus.read();
  auto res1 = modbus.read();

  if (res0 == -1  ||  res1 == -1)
    return false;

  memcpy(&arg, &res0, 2);
  memcpy((char*)&arg+2, &res1, 2);
  return true;
}
,