ESP8266 / Arduino Modbus RTU Буферное преобразование данных - PullRequest
0 голосов
/ 28 апреля 2019

Я использую библиотеку ESP8266 и ModbusMaster.h для связи с измерителем мощности с поддержкой RS485. Связь работает нормально, но ответы меня смущают, и я не могу получить правильные значения. Мой измеритель мощности показывает 1,49 кВтч, но ответ от Modbus - 16318. Вот мой код:

    #include <ArduinoOTA.h>
    #include <BlynkSimpleEsp8266.h>
    #include <SimpleTimer.h>
    #include <ModbusMaster.h>
    #include <ESP8266WiFi.h> 
    /*
    Debug. Change to 0 when you are finished debugging.
    */
    const int debug = 1; 
    #define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

    int timerTask1, timerTask2, timerTask3;
    float battBhargeCurrent, bvoltage, ctemp, btemp, bremaining, lpower, lcurrent, pvvoltage, pvcurrent, pvpower;
    float stats_today_pv_volt_min, stats_today_pv_volt_max;
    uint8_t result; 
    // this is to check if we can write since rs485 is half duplex
    bool rs485DataReceived = true;

    float  data[100];

    ModbusMaster node;
    SimpleTimer timer;

    // tracer requires no handshaking
    void preTransmission() {}
    void postTransmission() {}

    // a list of the regisities to query in order
    typedef void (*RegistryList[])();
    RegistryList Registries = { 
    AddressRegistry_0001       // samo potrosnju
    };
    // keep log of where we are
    uint8_t currentRegistryNumber = 0;
    // function to switch to next registry
    void nextRegistryNumber() {
    currentRegistryNumber = (currentRegistryNumber + 1) % ARRAY_SIZE( Registries);
    }

    void setup()
    {
    // Serial.begin(115200);
    Serial.begin(9600, SERIAL_8E1); //, SERIAL_8E1

    // Modbus slave ID 1
    node.begin(1, Serial);
    node.preTransmission(preTransmission);
    node.postTransmission(postTransmission);
    // WiFi.mode(WIFI_STA); 

    while (Blynk.connect() == false) {}
    ArduinoOTA.setHostname(OTA_HOSTNAME);
    ArduinoOTA.begin();

    timerTask1 = timer.setInterval(9000, updateBlynk); 
    timerTask2 = timer.setInterval(9000, doRegistryNumber);
    timerTask3 = timer.setInterval(9000, nextRegistryNumber);
    }

    // -------------------------------------------------------------------------------- 

    void doRegistryNumber() {
    Registries[currentRegistryNumber]();
    }


    void AddressRegistry_0001() {  

  uint8_t j;
  uint16_t dataval[2];

 result = node.readHoldingRegisters(0x00, 2); 
if (result == node.ku8MBSuccess)
{  
   for (j = 0; j < 2; j++)                        // set to 0,1 for two 
datablocks
    {
        dataval[j] = node.getResponseBuffer(j);
    }


     terminal.println("---------- Show power---------");
     terminal.println("kWh: ");
     terminal.println(dataval[0]);
     terminal.println("crc: ");
     terminal.println(dataval[1]);


     terminal.println("-----------------------"); 
     terminal.flush();   
     node.clearResponseBuffer();
     node.clearTransmitBuffer(); 
} else {
  rs485DataReceived = false;
} 

}

    void loop()
    {
    Blynk.run();
    // ArduinoOTA.handle();
    timer.run();
    }

Я пробовал подобную вещь, но с Raspberry Pi и USB-RS485, и она работает. Пример кода NodeJS приведен ниже. Это похоже на код Arduino.

    // create an empty modbus client
    var ModbusRTU = require("modbus-serial");
    var client = new ModbusRTU();

    // open connection to a serial port
    client.connectRTUBuffered("/dev/ttyUSB0", { baudRate: 9600, parity: 'even' }, read);

    function write() {
        client.setID(1);

        // write the values 0, 0xffff to registers starting at address 5
        // on device number 1.
        client.writeRegisters(5, [0 , 0xffff])
            .then(read);
    }

    function read() {
        // read the 2 registers starting at address 5
        // on device number 1.
            console.log("Ocitavanje registra 0000: ");
        client.readHoldingRegisters(0000, 12)
            .then(function(d) {
                var floatA = d.buffer.readFloatBE(0);
            // var floatB = d.buffer.readFloatBE(4);
            // var floatC = d.buffer.readFloatBE(8);
            // console.log("Receive:", floatA, floatB, floatC); })
            console.log("Potrosnja u kWh: ", floatA); })
            .catch(function(e) {
                console.log(e.message); })
            .then(close);
    }

    function close() {
        client.close();
    }

Этот код отображает 1.493748298302 в консоли.

Как я могу реализовать это var floatA = d.buffer.readFloatBE(0); в Arduino? Похоже, что readFloatBE(0) добивается цели, но доступно только в NodeJS / javascript.

Здесь я часть таблицы данных для моего устройства enter image description here

enter image description here

Вот что я получаю от оригинального программного обеспечения, поставляемого с устройством: enter image description here

Если бы кто-то мог направить меня в лучшем направлении, я был бы тогда счастлив.

UPDATE:

Я нашел программное обеспечение ShortBus Modbus Scanner и проверил показания. Результат чтения библиотеки в виде целого числа без знака, но нужно поменять местами с плавающей точкой и порядок слов. Это показано на изображении ниже.

Может кто-нибудь сказать, как установить правильное преобразование, пожалуйста.

enter image description here

Ответы [ 2 ]

0 голосов
/ 22 мая 2019

Вы пришли правильно с этой функцией?Я сталкиваюсь с той же проблемой и пытаюсь выяснить, как получить плавающее значение с двумя десятичными разрядами с адреса Modbus 8192. Это 2 блока данных.

void loop()
{
  uint8_t j, result;
  uint16_t dataval[2];

  result = node.readHoldingRegisters(8192, 2);      // slave: read (6) 16-bit registers starting at register .. to RX buffer , this address is in Decimal, so convert hex to decimal to use correct address
  if (result == node.ku8MBSuccess)                  // do something with data if read is successful
  {
    for (j = 0; j < 2; j++)                        // set to 0,1 for two datablocks
    {
      dataval[j] = node.getResponseBuffer(j);


    }

    //********************************
    String myValuea =  String(dataval[0], HEX); //Convert it into Hexadecimal
    String myValueb =  String(dataval[1], HEX); //Convert it into Hexadecimal
0 голосов
/ 01 мая 2019

Правильно, поэтому проблема действительно в части, выполняемой var floatA = d.buffer.readFloatBE(0); Modbus возвращает массив байтов, и клиент должен интерпретировать эти байты, в идеале это делается в используемой вами библиотеке, но если она недоступна в Arduino , вы можете попробовать вручную с функциями байтового декодирования, со следующими соображениями:

Регистры Modbus имеют длину 16 бит, поэтому длина 1 = 16 бит, длина 2 = 32 бита, следовательно, тип данных, отмеченный в документации как float32 означает Msgstr "2 регистра, используемых для этого значения, интерпретировать как float".

Следовательно, на client.readHoldingRegisters(0000, 12) вы просите прочитать регистр с адресом 00 и размером 12 ... так что это не имеет смысла, вам нужно только 2 регистра.

В вашем примере кода узла, сначала вы пишете 2 регистра по адресу 5 в client.writeRegisters(5, [0 , 0xffff]) зарегистрировать 5 = ​​0 и зарегистрировать 6 = 0xFFFF, почему? Тогда вы идете и читаете с адреса 0, в read (), который является адресом для общего кВтч на ваши документы.

Итак, вы должны получить массив байтов, и вам нужно расшифровывать их как поплавок. Modbus - это Big Endian для слов и байтов, поэтому Вы должны использовать их в функциях декодирования. Я точно не знаю что доступно в Arduino, но, надеюсь, вы можете понять это с этой дополнительной информацией.

Полагаю, что если вы просто отправите буфер на печать, вы получите целочисленную интерпретацию значения, отсюда и проблема

...