node-modbus-serial
node-modbus-serial copied to clipboard
Asking for suggestions for controlling GPIO pin for chip RE during serial RTU
Thank you for the library - it's really great. I'm experimenting with a model where I drive a GPIO high (RE) during the modbus write, and then drive it down again after I have written the bits. To this end, I have created a port called gpiortubufferedport:
// This is a version of the RTU serial buffered port but it uses a
// GPIO pin to module the Read Enable (RE) line
const rtubufferedport = require("./rtubufferedport");
const Gpio = require("onoff").Gpio;
var modbusSerialDebug = require("debug")("modbus-serial");
const SERIAL_WRITE_PADDING_MS = 2.0;
/**
*
* @param {string} path - the serial path
* @param {number} readEnableGpioPin - required.
* @param {object} options - we maintian the options model
*/
const gpiortubufferedport = function(path, readEnableGpioPin, options) {
modbusSerialDebug("Creating GPIO-based serial connection to", path, "with pin", readEnableGpioPin);
rtubufferedport.call(this, path, options);
// Now we need to override the write method to drive the gpio read-enable low
this.readEnableGpio = new Gpio(readEnableGpioPin, "out");
// Start with it low
this.readEnableGpio.writeSync(0);
this.write = function(data) {
modbusSerialDebug("Writing", data, "to", path);
// Overwrite the buffer with a new one so we start from scratch since we know that
// we're doing this serially here
this._buffer = Buffer.alloc(0);
// We need to wait for the buffer to actually be written to the serial port before we clear flip read-enable back to low
// There are a couple of models for doing this - we could use drain, but it seems that it takes too long
// and the remote device has already started responding by then
// this._client.drain(() => {
// this.readEnableGpio.writeSync(0);
// modbusSerialDebug("Done writing to", path);
// });
// Instead I'm hacking it for now to estimate the time it takes to write the bits (assuming it starts immediately)
// based upon the baud rate;
const numBits = data.length * 10;
// the baud rate is bits/sec so to get the number of ms we do
// For example, at 38400 baud, writing a buffer of length 10 will tak approximately 2.5 seconds (10*10*1000)/38400;
// We add a few more ms to be sure (but this has to be set low enough so that the read-enable is down by the time the remote
// device starts transmitting
let paddingMs = options.paddingMs == null ? SERIAL_WRITE_PADDING_MS : +options.paddingMs
const duration = (numBits * 1000) / (options.baudRate || 9600) + paddingMs;
modbusSerialDebug("Setting RE duration to", duration,`based upon ${paddingMs} padding`);
setTimeout(() => {
this.readEnableGpio.writeSync(0);
modbusSerialDebug("Done writing to", path);
}, duration);
this.readEnableGpio.writeSync(1);
gpiortubufferedport.prototype.write.call(this, data);
};
};
gpiortubufferedport.prototype = Object.create(rtubufferedport.prototype);
gpiortubufferedport.prototype.constructor = gpiortubufferedport;
module.exports = gpiortubufferedport;
which works well enough for many devices but if the device responds very quickly then the RE is still high because the use of a timeout to control timing is not precise enough. Would you have any suggestions on how to achieve more accurate timing so that we can drive the RE low immediately after the modbus request has gone out?
Can you please provide more details about the machine and OS you are using.
Doh! Sorry. That's pretty important info - this is on a raspberry pi4 running ubuntu.
I don’t think this can be achieved with a userspace application, which nodejs is on an Ubuntu machine. Please read this: https://minimalmodbus.readthedocs.io/en/stable/serialcommunication.html. It might help to understand the issue
If you try to use a hardware solution with a retriggerable multivibrator, this will lead to solution more quickly. I use a USB RS485 converter, with the enable circuit embedded.