Skip to content
JamesNewton edited this page Aug 6, 2020 · 17 revisions

There are two ways to connect to a ModBus device: Serial and TCP. In either case, the protocol needs to be processed via an NPM Module which can be integrated into the DDE Job Engine and which has been added to DDE for the PC as of version 3.5.7 (Note: This is a BETA version).

Module

If your job engine doesn't already have it, you will need to install support for ModBus protocol into Dexter. We have tested the modbus-serial NPM package. Despite the name, modbus-serial supports both serial and TCP connections.

You can do this directly if Dexter is connected to the internet by SSHing into Dexter and then:

cd /root/Documents/dde
npm install modbus-serial

Note: The install will take a while.

If your Dexter is NOT directly connected to your PC and does not have ModBus installed, you can try to install it locally, and then copy the files over into the node_modules folder and trigger the make in the node_modules/modbus-serial/node_modules/@serialport/bindings/build folder. This is not well documented. Hopefully you can get an updated image with the ModBus support pre-installed, or connect your Dexter to the internet for this process.

Once the module is installed, you can use it via node.js. Examples are found here:
https://github.com/yaacov/node-modbus-serial/tree/master/examples

Job Engine Support

To access the new module from DDE jobs, until support is built in, we need to update the core files SSHing into Dexter and then:

cd /root/Documents/dde
nano core/index.js

Scroll down to the global. section near the end and add the line:
var ModbusRTU = require("modbus-serial") after the last var line above the global section, then below the other globals, add:
global.ModbusRTU = ModbusRTU

After this change, any of the methods allowed by the library should be available in a DDE job via the ModbusRTU object.

ModBus TCP

To access network modbus devices (other than you own PC), your Dexter will need to be connected to the network. The following classes are supported, when working as a client:

Class Client Function
FC1 "Read Coil Status" readCoils(coil, len)
FC2 "Read Input Status" readDiscreteInputs(addr, arg)
FC3 "Read Holding Registers" readHoldingRegisters(addr, len)
FC4 "Read Input Registers" readInputRegisters(addr, len)
FC5 "Force Single Coil" writeCoil(coil, binary) //NOT setCoil
FC6 "Preset Single Register"
FC15 "Force Multiple Coil" writeRegister(addr, value)
FC16 "Preset Multiple Registers" writeRegisters(addr, valueAry)
FC43/14 "Read Device Identification" (supported ports: TCP, RTU) readDeviceIdentification(id, obj)

For operation as a server, accepting requests from other modbus devices, see Issue 34 for progress on integrating a modbus server into the "always on" node.js web server on Dexter, and see this more extensive example from DCISIV for two way modbus communications in a job engine job.

//place this in /srv/samba/share/dde_apps/modbus.dde
//var ModbusRTU = require("modbus-serial") //for node.js, not needed in DDE
var client = new ModbusRTU();
var plc = "192.168.0.177"; //change to the address of the remote device.

// open connection to a tcp line
client.connectTCP(plc, { port: 8502 });
client.setID(1);
client.setTimeout(3000);
out(client)

client.callback = function(err, data) {
	if (err) {
    	Robot.error(err); //change Robot to Control in future versions
        }
    else {
        console.log(data.data);
        }
    }

new Job({ user_data: {state: 1},
    name: "modbus",
    do_list: [
    	function() { client.writeCoil(5, this.user_data.state, client.callback)},
        function() { this.user_data.state = 0 },
        Robot.wait_until(1), //change Robot to Control in future versions
    	function() { client.writeCoil(5, this.user_data.state, client.callback)},
    ]})

//to run this from the terminal prompt (ssh in to Dexter), enter:
//node core define_and_start_job /srv/samba/share/dde_apps/modbus.dde
//Once that is working, you can add it to RunDexRun (see the bottom) or later to autoexec.jobs (if you have that)

A more extensive example from DCISIV is available.

A good tool for this is QModMaster which can simulate a ModBus master and allow you to send test messages.

When working with ModBusTCP, it would be best for Dexter to be listening for a message all the time, and to start a job if a new message is recieved. This ability will be integrated into the onboard node web server in time. See Issue 34 for progress.

ModBus Serial

Of course, accessing a serial modbus device requires a serial adapter, either one with built in RS485 support 1, or standard serial unit 1, 2, 3 with a TTL to RS485 adapter 1. USB adapters based on the CP2102 or CP2104 are known to work with Dexter.

See Dexter-Serial-Peripherals for installation, testing and troubleshooting of serial adapters in general.

This example job connects to a serial adapter at /dev/ttyUSB0 and once the connection is established, it turns on the relay coil, reads the switch input and then turns off the relay. It uses a device address of 0 which seems to work with the standard cheap chinese modbus devices^.

const serial_path = "/dev/ttyUSB0"
const client_id = 0;
const coil_no = 0;

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

// open connection to a serial port, then callback write
client.connectRTUBuffered(serial_path, { baudRate: 9600 }, write);

//connection open, set client address and write
function write() {
    client.setID(client_id);

    // write the value to the coil
    client.writeCoil(coil_no, 1)
        .then(read) //promise to read
    console.log("write done");
    return
    }

function read() {
    // read the register
    client.readHoldingRegisters(0,1)
        .then(function(data){
                console.log(data)
                client.writeCoil(coil_no, 0)
                    .then(finish)
                });
    }

function finish() {
        client.close()
        console.log("finished")
        }
Clone this wiki locally