Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swapped float point #506

Open
giegiey opened this issue Jul 27, 2023 · 14 comments
Open

Swapped float point #506

giegiey opened this issue Jul 27, 2023 · 14 comments

Comments

@giegiey
Copy link

giegiey commented Jul 27, 2023

Hello, I tried to convert the data result to swapped float point but it did not work .

image

This is the result using modscan32.

My code

const {
    SerialPort
} = require('serialport')
const ModbusMaster = require('modbus-serial')

// Function to swap bytes (big-endian to little-endian)
function swapBytes(buffer) {
    const len = buffer.length
    for (let i = 0; i < len - 3; i += 4) {
        const temp = buffer[i]
        buffer[i] = buffer[i + 3]
        buffer[i + 3] = temp
        const temp2 = buffer[i + 1]
        buffer[i + 1] = buffer[i + 2]
        buffer[i + 2] = temp2
    }
}

// Function to convert a 4-byte buffer to a 32-bit floating-point number
function bufferToFloat(buffer) {
    swapBytes(buffer); // Swap bytes before converting to float
    return buffer.readFloatBE(0) // Interpret the buffer as a big-endian float
}

//read register data from digital meter using modbus-serial package
const readRegistersData = async (id, inputLength) => {
    return new Promise(async (resolve, reject) => {
        // set ID of slave
        await master.setID(id)
        // read the 1 registers starting at address 0x00(first register)
        await master.readInputRegisters(1, inputLength).then((res) => {
            //resolve show result
            resolve(res)
        }, (e) => {
            //reject show error
            reject(e)
        })
    })
}

//run and check if serial port is found on device
SerialPort.list().then(
    ports => {
        //get all port list
        ports.forEach(async port => {
            //find device name
            if (port.manufacturer.includes("wch.cn")) {
               // open connection to a serial port
                master = await new ModbusMaster()
                await master.connectRTUBuffered(port.path, {
                    baudRate: 9600,
                    parity: 'even'
                })
                readRegistersData(12, 55).then((recvData) => {
                    const floatValues = []
                    for (let i = 0; i < 55; i++) {
                        const buffer = Buffer.from(recvData.buffer.slice(i * 4, (i + 1) * 4)) // Read 4 bytes for each float value
                        floatValues.push(bufferToFloat(buffer))
                        console.log(bufferToFloat(buffer))
                    }
                })
            }
        })
    },
    error => {
        console.log(error)
    })

image

How to get data result in swapped float point. Please advise this

@aaaatttyy
Copy link

Hi any updates on this I have a similar use case ?

@yaacov
Copy link
Owner

yaacov commented Sep 16, 2023

try not to swapBytes yourself, find a buffer to float method, that swap it for you:
https://www.geeksforgeeks.org/node-js-buffer-readfloatbe-method/

@aaaatttyy
Copy link

try not to swapBytes yourself, find a buffer to float method, that swap it for you: https://www.geeksforgeeks.org/node-js-buffer-readfloatbe-method/

Sorry not able to get your point, my usecase is
We get a buffer from readInputRegisters but since smartEnergyMeter specifies only flaot values to be used as also depicted in the screenshot above I need you recommendation on how to proceed to get the desired output like 240.234, by utilising the buffered returned by the readInputRegister API.

@yaacov
Copy link
Owner

yaacov commented Sep 17, 2023

here is an example:
https://github.com/yaacov/node-modbus-serial/blob/master/examples/buffer.js

you need to remove the swapBytes method and use one of nodejs built in methods to do the swapping for you.

a. test that the raw data you get is the same the one in modscan, e.g. hex in modscan, and see that the hex value are the same for the same address.
b. different ways to read the data, did you try use big-endian instead of little? https://nodejs.org/api/buffer.html#bufreadfloatbeoffset

@aaaatttyy
Copy link

https://github.com/yaacov/node-modbus-serial/blob/master/examples/buffer.js

yes I tried these methods already but all of these return a 32 bit int value whereas I wanted it to be something of sort decimal like 240.324 etc.
For point a ,I tested in modscan the hex values are exactly the same,hence quite confused with this one.

@yaacov
Copy link
Owner

yaacov commented Sep 17, 2023

have you tried readFloatBE ?

@aaaatttyy
Copy link

have you tried readFloatBE ?

yes I tried returns same 32 bit

@aaaatttyy
Copy link

aaaatttyy commented Sep 18, 2023

{ data: { data: [ 43909, 0, 0, 0, 0, 17059, 51118, 17269, 43909, 0 ], buffer: <Buffer ab 85 00 00 00 00 00 00 00 00 42 a3 c7 ae 43 7 5 ab 85 00 00> } }

@yaacov
Copy link
Owner

yaacov commented Sep 18, 2023

I'm adding a help wanted because your problem sound very weird

this is how 240.324 looks fp and swapped fp:
hex: 4370 52f2
fp: buf.readFloatBE(0) = 240.32400512695312
swapped fp: buf.readFloatLE(0) = -4.168164361781354e+30

in your example I can't find a 2 registrar (32 bit) hex that can represent a 0 to 1000 number using swapped fp (readFloatLE)

assuming this is the same hex input used by modscan, I'm adding a help wanted in case someone knows what the issue is

@aaaatttyy
Copy link

I pasted a console output of data being received from the meter device from which I want to infer the floating voltage value,
would be really helpful if you have any idea on this type of data being thrown by device .

@yaacov
Copy link
Owner

yaacov commented Sep 18, 2023

const hexString = 'ab85000000000000000042a3c7ae4375ab850000';
const buf = Buffer.from(hexString, 'hex');

for (let i = 0; i < buf.length - 3; i++) {
    const floatValue = buf.readFloatLE(i);
    if (floatValue >= 0 && floatValue <= 1000) {
        console.log(`Found float value ${floatValue} at position ${i}`);
    }
}

hmm, i can actually find some numbers:

Found float value 4.795103215073092e-41 at position 0
Found float value 1.8637269575520067e-43 at position 1
Found float value 0 at position 2
Found float value 0 at position 3
Found float value 0 at position 4
Found float value 0 at position 5
Found float value 0 at position 6
Found float value 32 at position 7
Found float value 349.5596618652344 at position 11
Found float value 1.227562818250744e-38 at position 15
Found float value 4.795103215073092e-41 at position 16

@yaacov
Copy link
Owner

yaacov commented Sep 18, 2023

so using your input in position 11 we have somthing that looks like usefull data ...

@sigma-design
Copy link
Contributor

sigma-design commented Sep 18, 2023

Historically Modbus RTU was designed only for integer value type using 2-byte 'registers', so there was no definite specification for 4-byte IEEE-754 floating point values. As the result, there are 4 different ways to interpret/convert bytes buffer into IEEE-754 floating point values, see for example settings in the Modbus Poll tool:
image

Also Modbus RTU uses "Register numbering" starting from 1 while the actual addressing starts from 0, e.g. Register 30001 means that Modbus RTU reading address should be set to 0 (3xxxx means using "Read Input Registers" command) - this also may lead to incorrect data reading/conversion.

There is a very handy conversion tool https://www.h-schmidt.net/FloatConverter/IEEE754.html that can be used to try different combinations of bytes (from your data buffer) and see which one makes sense.

For some reason your example buffer is not multiple of 4x - either there is an additional one byte or missing three bytes.

@sigma-design
Copy link
Contributor

sigma-design commented Sep 18, 2023

I am using this library for a while reading floating points values from our instruments, so please consider the following that may be helpful in your case:

  1. Make sure that your data buffer for floats has an appropriate number of bytes (multiple of 4).
  2. Make sure that a correct starting address is used.
  3. Try different 32 Bit Float conversion formats (there are four of them) since it's not stipulated by Modbus RTU standard and companies can implement it as they see fit for their business. You can try these four potential formats by combining swap16() and readFloatLE()/readFloatBE() functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants