morkai / h5.modbus

Implementation of the MODBUS IP/ASCII/RTU master and slave over TCP/UDP/Serial for Node.js.
https://miracle.systems/p/h5.modbus
MIT License
28 stars 21 forks source link

Documentation #1

Open goranach opened 11 years ago

goranach commented 11 years ago

I am very interested in your project, as it only meets my requirements. Could you write a small documentation for me. Thanks in advance.

morkai commented 11 years ago

Hey!

I've added a few examples to the example/ directory. Check out the verbose-init-tcp-ip.js especially, as it also has some comments.

The automated tests are not finished, so there may be bugs. Some features (like functions for handling of repeatable transactions) are not yet present.

As I was writing the code, I've tested it against Modbus PLC Simulator (can be used to quickly run the examples), some Delta PLC (Serial+ASCII, Serial+RTU, TCP+IP), Array APB PLC and Delta+Moxa NPort (TCP+ASCII -> Serial+ASCII, UDP+ASCII -> Serial+ASCII...).

The proper readme and documentation will be done after 100% test code coverage, which in turn may be done if I'll have to do another application that requires MODBUS.

Until then, feel free to ask any questions here ;)

rlemon commented 10 years ago

Do you have any examples using Serial RTU? The project looks pretty solid however I am struggling with reading and writing holding registers over serial RTU. Any small example using your project would be wonderful.

morkai commented 10 years ago

You'll need node-serialport:

npm install serialport

Then, create the master like so:

var SerialPort = require('serialport').SerialPort;
var modbus = require('h5.modbus');

var serialPort = new SerialPort('/dev/ttyUSB0', {
  baudRate: 9600,
  dataBits: 8,
  stopBits: 2,
  paritry: 'none'
});

var master = modbus.createMaster({
  transport: {
    type: 'rtu',
    eofTimeout: 10, // End of frame timeout
    connection: {
      type: 'serial',
      serialPort: serialPort
    }
  }
});

// ...
rlemon commented 10 years ago

Thanks for the quick reply. I will fiddle about and let you know how everything goes.

rlemon commented 10 years ago

Thanks again for the help. One more question (then I think I am good to get this rolling on my own).

I am attempting to connect to my device and just read a single holding register, however immediately after running master.readHoldingRegisters(1,1,opts) i'm receiving a timeout error (no delay is waited) - shortly after the retries and stuff happen.

events.js:72
        throw er; // Unhandled 'error' event
              ^
ResponseTimeoutError: No response was received from the slave in the specified time.
    at Transaction.handleTimeout (/home/rlemon/dryermaster/modbus/lib/Transaction.js:452:20)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

Have you ever seen this while working with the library? What was the general cause of it to not want to communicate? I have tested that the device does communicate properly on windows with other software via modbus RTU.

I appreciate your time.

Thanks, rlemon.

morkai commented 10 years ago

You have to listen for the error events on the transaction object returned by the readHoldingRegisters() or set the master's supressTransactionErrors to true (master and transaction objects are node.js EventEmitters and they throw an exception if an error event was emitted, buy there is no listeners):

// ...

var master = modbus.createMaster({
  suppressTransactionErrors: true, // automatically adds a listener for the `error` events to every transaction
  transport: {
    // ...
  }
});

var req = master.readHoldingRegisters(1, 1, opts);

req.on('error', function(err)
{
  console.error(err.message);
});

If you have a lot of timeout errors, then try to increase the timeout options until they are gone (master's defaultTimeout option or transaction's timeout option).

rlemon commented 10 years ago

Sorry to continue to bother you. We're having some real issues - I presumed to change the slaveID I would change the unit in the Transaction Options.. but reading the stream on the other end I'm seeing the slave as 00 ?? How do I set the slave ID?

morkai commented 10 years ago
master.readHoldingRegisters(0, 1, {unit: 1})

What Connection and Transport are you using? Serial RTU?

rlemon commented 10 years ago

Sorry false alarm. I have another gentleman working on this with me and he hard coded the unit in the Transaction file to 13 (and tera term makes D and 0 look incredibly alike) - So we were actually seeing 0D (13) - reverted the file and it works as expected. Sorry for jumping the gun.

paradigminversion commented 10 years ago

Sorry, to bother you. I'm working along side rlemon on this code. Unfortunately, as it stands the modbus library defaults to START_LOW_COIL = 1, but we want it to start at 0. Here's my calling procedure:

    var transactionOptions = {
        timeout: 10000,
        unit: 1
    };

    var req = master.readHoldingRegisters( 1 , 1 , transactionOptions);

Is there a default variable, analogous to 'timeout' or 'unit' in the code sample above, that we can specify to force it to start at 0?

The frame data it currently outputs is: "01 03 00 01 00 01 D5 CA".

Thanks

morkai commented 10 years ago

Why don't you pass 0 as the first argument?

// readHoldingRegisters(address, quantity, options)
master.readHoldingRegisters(1, 1, {unit: 1})
// results in frame
// 01 - unit
// 03 - function code
// 00 01 - address
// 00 01 - quantity
// D5 CA - checksum

master.readHoldingRegisters(0, 1, {unit: 1})
// should result in frame
// 01 - unit
// 03 - function code
// 00 00 - address
// 00 01 - quantity
// xx xx - checksum
paradigminversion commented 10 years ago

Awesome!!! It works!!! Thanks a bunch.

paradigminversion commented 10 years ago

I have a terminal monitoring packets sent and received and I am finally getting a response packet from the slave of: 01 03 02 00 00 B8 44 which looks perfect and the CRC checks out, but the modbus code doesn't receive anything. It just times out with the message: 'ERR [ { name: 'ResponseTimeoutError', message: 'No response was received from the slave in the specified time.' }, undefined, undefined ] ' Again the code is,

var SerialPort = require('serialport').SerialPort,
    modbus = require('./lib');

var portOptions = {
    baudRate: 19200,
    dataBits: 8,
    stopBits: 1,
    parity: 'none',
    flowControl: false
};

var serialPort = new SerialPort('/dev/ttyO2', portOptions, true, startScan);

function startScan() {
    var master = modbus.createMaster({
        suppressTransactionErrors: true,
        transport: {
            type: 'rtu',
            eofTimeout: 100000,
            connection: {
                type: 'serial',
                serialPort: serialPort
            }
        }
    });

    var transactionOptions = {
        timeout: 10000,
        unit: 1
    };

    var req = master.readHoldingRegisters( 0 , 5 , transactionOptions);

    req.on('response', function(a,b,c) {
        console.log('RESP', [a,b,c]);
    }).on('complete', function(a,b,c) {
        console.log('COMPL', [a,b,c]);
    }).on('error', function(a,b,c) {
        console.log('ERR', [a,b,c]);
    });

}

Is there anyway that I can dump or check if data is received? Thanks in advance.

morkai commented 10 years ago

Modbus RTU separates frames by not sending anything for a specific amount of time ("at least 3 1⁄2 character times of silence between frames", see Wikipedia). I don't think you can wait 3 1/2 character times in Node.js so I've introduced the eofTimeout option. After the data is received, the RTU transport waits eofTimeout milliseconds, and if no more data is received, then an end of frame is assumed. If more data arrives before the eofTimeout ms, then the end of frame timer is restarted.

You have set the eofTimeout to 100 seconds, so the RTU transport will parse the frame after 100s. BUT You have also set the timeout to 10 seconds, which is less than eofTimeout and finishes first. There's your ResponseTimeoutError.

Try setting the timeout option to 200 and the eofTimeout to 20. If you'll receive a lot of ResponseTimeoutErrors, then slowly increase the timeout option. If you'll receive a lot of IncompleteResponseFrameErrors or InvalidChecksumErrors, then slowly increase the eofTimeout option.

paradigminversion commented 10 years ago

That did it!!! Thanks again.

tvthatsme commented 10 years ago

Hey, do you have any examples of using ascii?

I have been successfully using your rtu example but switching over to ascii I keep getting a "ResponseTimeoutError". I connected a network analyzer and my device is getting everything correct, I just can't figure out how to tell the listener that there is a response waiting.

morkai commented 10 years ago

Hey! Try increasing the master's defaultTimeout option or transaction's timeout option. See https://gist.github.com/morkai/f7b11f971682fc8e5598

tvthatsme commented 10 years ago

Thanks for the quick response! Did a complete wipe of my source and that fixed it. I must have tried modifying something in the h5.modbus source. Responses are coming through in ~50 ms now so the defaultTimeout wasn't the issue (I was...).

zachheine commented 9 years ago

Should 'eofTimeout' be a function of the baud rate?...

morkai commented 9 years ago

It should, but it won't work in node.js. For baud rates >= 19200 it should be 1.75 ms, for 9600 - 3.65 ms. Node isn't reliable with such small timers, so you should just test what is the smallest value that works for you.

lamyra commented 9 years ago

Hey, I try to make scada app in node.js ,i need modbus-tcp but i haven't find any example that explain modbus with node.js , i have installed modbus-tcp module but i don't find an example and modbus simulator ?? Please i need your help

morkai commented 9 years ago

Look into the example/ directory or check out any project of mine that is using this library.

AllenBird commented 8 years ago

I am interested in this project. but the node version is too high. is V0.10.X will be possible?

morkai commented 8 years ago

The one from the master branch won't work (unless you transpile it using something like Babel).

The old version will work: https://github.com/morkai/h5.modbus/tree/v0