Преобразование десятичных данных rtu Modbus в число с плавающей запятой

Привет,я пытаюсь связаться с контроллером коэффициента мощности [Selec APFC 148-312] с помощью modbus RTU,я следую примеру скетча из библиотеки modbusmaster. я могу получить необработанные данные с прибора, но структура данных, похоже, в десятичном формате, может кто-нибудь помочь мне преобразовать это в float.спасибо

#include <ModbusMaster.h>


#define MAX485_DE      3
#define MAX485_RE_NEG  2

// создать экземпляр объекта ModbusMaster
ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Инициализация в режиме приема

  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // вязь по Modbus работает со скоростью 9600 бод
  Serial.begin(9600);

  // Идентификатор ведомого устройства Modbus 1
  node.begin(1, Serial);
  // Обратные вызовы позволяют нам правильно настроить передатчик RS485
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  float result;

  result = node.readInputRegisters(0x30000, 10);
  if (result == node.ku8MBSuccess)
  {

    Serial.print("V1N: ");
    Serial.println(node.getResponseBuffer(0x01));

    Serial.print("V2N: ");
    Serial.println(node.getResponseBuffer(0x02));
    
    Serial.print("V3N: ");
    Serial.println(node.getResponseBuffer(0x03));

    Serial.print("Vavg: ");
    Serial.println(node.getResponseBuffer(0x04));

    Serial.print("V12: ");
    Serial.println(node.getResponseBuffer(0x05));

    Serial.print("V23: ");
    Serial.println(node.getResponseBuffer(0x06));
    
    Serial.print("V31: ");
    Serial.println(node.getResponseBuffer(0x07));

    Serial.print("L-L avg: ");
    Serial.println(node.getResponseBuffer(0x08));
  }
  delay(1000);
}

, 👍2

Обсуждение

Вопрос программирования на C++... на самом деле не совсем конкретный Arduino, @jsotola

десятичный формат и формат с плавающей запятой-это две разные вещи ... что вам на самом деле нужно?, @jsotola

@jsotola мне просто нужно, чтобы вывод, отображаемый на последовательном мониторе, отображал показания напряжения и других параметров, как указано в техническом паспорте., @Ragaven N

Re “структура _data, похоже, имеет десятичный формат_”: Это не так. Это двоичный файл. Причина, по которой вы видите десятичное число на последовательной консоли, заключается в том, что Serial.println() преобразует свой аргумент в десятичное число для печати в виде текста. Авторы этого метода предположили, что большинство людей предпочитают читать десятичные, а не двоичные числа. Обратите внимание, что " Serial.println()` может быть указано (вторым аргументом) использовать другую базу., @Edgar Bonet


2 ответа


2

Я не уверен, что это действительно можно считать актуальным, но кто-то, кто хочет читать числа с плавающей запятой по MODBUS с помощью Arduino, может что-то из этого извлечь. Кроме того, у меня нет возможности проверить это на самом деле. Но вот что происходит:

Ваша карта MODBUS показывает V2N на 30002, и вы заметили, что 30003 не упоминается на карте. Ваше напряжение со второй фазы на нейтраль, по-видимому, кодируется в два последовательных 16-разрядных регистра, что является несколько нормальной схемой для передачи 32-битных float через MODBUS, насколько я могу судить. Итак, ваши значения 17280 и 9830 являются двумя половинками 32-разрядного числа с плавающей запятой. В шестнадцатеричном формате 0x4380 и 0x2666. Они, по-видимому, взяты вместе как 0x43802666 в качестве битового шаблона для 32-разрядного float.

Подобная функция позволит вам извлекать 32-разрядные данные с плавающей точкой из отдельного 16-разрядного регистра MODBUS, который гласит:

float modbus_16bit_register_pair_to_float(uint16_t a, uint16_t b) {
    uint32_t combined = ((uint32_t)a << 16) | b;
    float f;
    memcpy(&f, &combined, sizeof f);
    return f;
}

Есть много способов, которыми вы могли бы это сделать. Этот код предполагает, что конец типа float совпадает с концом типа uint32_t, и что float 32-разрядный, что является обычным, но не строго стандартным.

Запуск этого на 17280 и 9830 дает 256,3 (округлено), что не совсем соответствует другим выводам, но я подозреваю, что это просто потому, что показания различаются.

,

Спасибо, Сэр. это сработало., @Ragaven N


1
#include <SimpleModbusMaster.h>


#define TxEnablePin 2                    // штифт управления направлением RS485 modbus:
#define baud 19200                           // modbus-порт:
#define timeout 2000                        // время ожидания modbus в мСек:
#define polling 200                         // modbus в МС:
#define retry_count 100

#define TOTAL_NO_OF_REGISTERS 2// количество регистров для опроса:



enum
{
PACKET1,
PACKET11,
TOTAL_NO_OF_PACKETS                       // оставьте эту последнюю запись
};
Packet packets[TOTAL_NO_OF_PACKETS];        // массив пакетов, которые необходимо настроить
const int ledPin =  LED_BUILTIN;
unsigned int regs[TOTAL_NO_OF_REGISTERS];   // массив главных регистров
long previousMillis = 0;
long interval = 1500;
unsigned long currentMillis;


void setup() {
pinMode(LED_BUILTIN, OUTPUT);

Serial.begin(9600);

// Регистры состояния Realay и DI:

 modbus_construct(&packets[PACKET1], 1, READ_INPUT_REGISTERS, 87,2,1); // eg :   

// Регистры счетчиков энергии APFC :
 modbus_construct(&packets[PACKET11], 1, READ_INPUT_REGISTERS, 7, 2,21);

  
 modbus_configure(&Serial, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets,TOTAL_NO_OF_PACKETS, regs); //  Инициализировать запрос Modbus к ведомому устройству
}

void loop() { 
 modbus_update();
 int DI;
 unsigned long a = (unsigned long)regs[1] << 16 | regs[2];
 DI = *(int*)&a;

float VN;
unsigned long k = (unsigned long)regs[21] << 16 | regs[22];
VN = *(float*)&k;

  currentMillis = millis();
 if (currentMillis - previousMillis >= interval)
 {  

 Serial.print("Состояние АВТО/ВРУЧНУЮ: "); // 0=выкл., 1=ВКЛ.
Serial.println(DI);

Serial.print("V-N :");
Serial.println(VN);

previousMillis = currentMillis;
 }

}

работает, как ожидалось, но может ли кто-нибудь показать, как печатать слово вместо 1, если возвращаемое значение равно 1. спасибо.

,

Если это действительно предназначено для ответа на ваш собственный вопрос, в коде должно быть какое-то объяснение. Приведенный вами код предполагает, что вы уже понимаете, как можно печатать разные вещи на основе значения переменной. Однако, если вы хотите спросить об этом, это должно войти в новый вопрос. Но я подозреваю, что вам на самом деле не нужно спрашивать, и вы можете решить это из официальных [примеров](https://www.arduino.cc/en/Tutorial/BuiltInExamples) и [документация](https://www.arduino.cc/reference/en/)., @timemage