yaacov / node-modbus-serial

A pure JavaScript implemetation of MODBUS-RTU (and TCP) for NodeJS
ISC License
627 stars 239 forks source link

CRC error in serial modbus? #326

Open PAIDI-RAMESH opened 4 years ago

PAIDI-RAMESH commented 4 years ago
var ModbusRTU = require("modbus-serial");
var client = new ModbusRTU();

// open connection to a serial port
client.connectRTUBuffered(
   "COM1", { baudRate: 9600,parity: 'none', dataBits : 8,stopBits : 1 }, read);

 function read() {
        client.readDiscreteInputs(1, 1)
        .then(function(data) {

           console.log(data);
           client.close();
        }).catch(err=>{console.log("error  "+err)});

}
yaacov commented 4 years ago

Thank you for the issue.

a. what error do you get ? b. can you run with debug on ? c. how often do you get the error ?

PAIDI-RAMESH commented 4 years ago

image

yaacov commented 4 years ago

can you send a pull request fixing it ?

exitrip commented 4 years ago

I'm running this on a oscilloscope and can see that the modbus-serial code is transmitting 16 bit numbers for every register address and data field over the wire. I can't tell what the module expects with the oscilloscope.

This would obviously mess up the CRCs if the device was using 8 bit registers.

(Un?)fortunately, I am definitely not a javascript programmer and have no business fixing this for others. An FYI, the module definitely doesn't strictly enforce uint8_t in all the things.

alexszilagyi commented 4 years ago

@exitrip : I'm a software developer and I'm facing the same thing on my project. Do you think you can help me with the debugging sessions? I'm not that good at electronics but did some studies previously. I'm only interested how did you've figure it out and to to debug actually. I am looking forward to fix this and add a PR to the repo, @yaacov .

exitrip commented 4 years ago

@alexszilagyi I ended up implementing everything using https://github.com/serialport/node-serialport directly and grabbing CRC javascript off a few websites. Unfortunately can't share the whole working source, as it's for a client, but I'll paste some boilerplate and open snippets:

const SerialPort = require('serialport')
const InterByteTimeout = require('@serialport/parser-inter-byte-timeout')

const portPath = '/dev/ttyUSB1';

var port = new SerialPort(portPath, {
  baudRate: 9600,
  dataBits: 8,
  parity: 'none',
  stopBits: 1,
  flowControl: false
})

/*
* The recieve thread
*/
parser.on('data', function (data) {
    //format the data buffer as a hex string and calc the crc
    for (let hex of hexFormatValues(data)) {
      rx_packet += hex;
    } 
    rx_crc = calculateCRCString(rx_packet);

    //because of the magic of CRCs, if we calculate a '0' crc
    //  we have recieved a valid packet (or all 0's)
    //  we also reset the read if we somehow got any nulls
    //  (these things happen)
    if(parseInt(rx_crc, 16) === 0 || !rx_crc ||
      !rx_packet || parseInt(rx_packet, 16) === 0) {
    //  TODO process rx packet and then zero it out
        if (parseInt(rx_crc, 16) === 0) {
            //TODO this is where you have a valid response
            console.log('RX: ', rx_packet);
        }
        rx_packet = '';
        console.log('-')
    }
})

/*
*   Stock/boilerplate serialport code
*/
// Open errors will be emitted as an error event
port.on('error', function(err) {
  console.log('Error: ', err.message)
})

/*
*  Functions of convenience I scraped off the internet and modified
*/

function calculateCRCString ( input ) {
    var crc = 0xFFFF;
    //no input verification TODO
    var str = hexStringToString(input);
    for (var pos = 0; pos < str.length; pos++) {
        crc ^= str.charCodeAt(pos);
        for (var i = 8; i !== 0; i--) {
            if ((crc & 0x0001) !== 0) {
                crc >>= 1;
                crc ^= 0xA001;
            } else
                crc >>= 1;
        }
    }
    //crc is a number,
    //  we need to build a string with swapped bytes for modbus
    //  Modbus is LSB first!!!  Do this to every 16 bit number
    crc = ((crc & 0x00ff) << 8) + ((crc & 0xff00) >> 8);
    return crc.toString(16);
}

function hexStringToString (inputstr) {
    var hex = inputstr.toString(); //force conversion
    var str = '';
    for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
}

function* hexFormatValues(buffer) {
  for (let x of buffer) {
    const hex = x.toString(16)
    yield hex.padStart(2, '0')
  }
}