Преобразование десятичных данных 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);
}
@Ragaven N, 👍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
#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
- Основная связь Arduino ModBus RTU с проблемой измерителя мощности
- Мониторинг контроллера Modbus RTU с помощью Arduino и модуля RS485
- Связь Arduino Uno и ESP32 с использованием RS485
- Когда дело доходит до связи UART-RS485, в чем разница между модулем "MAX485" и модулем "HW-0519"?
- Управление VFD с помощью ModBus RTU через RS485 и Arduino
- Взаимодействие NodeMCU Modbus с Multi-Switch давления
- Использование Modbus-RTU с Arduino и контроллером температуры
- считывание данных с контроллера через RS485 Modbus rtu Arduino
Вопрос программирования на C++... на самом деле не совсем конкретный Arduino, @jsotola
десятичный формат и формат с плавающей запятой-это две разные вещи ... что вам на самом деле нужно?, @jsotola
@jsotola мне просто нужно, чтобы вывод, отображаемый на последовательном мониторе, отображал показания напряжения и других параметров, как указано в техническом паспорте., @Ragaven N
Re “структура _data, похоже, имеет десятичный формат_”: Это не так. Это двоичный файл. Причина, по которой вы видите десятичное число на последовательной консоли, заключается в том, что
Serial.println()
преобразует свой аргумент в десятичное число для печати в виде текста. Авторы этого метода предположили, что большинство людей предпочитают читать десятичные, а не двоичные числа. Обратите внимание, что " Serial.println()` может быть указано (вторым аргументом) использовать другую базу., @Edgar Bonet