#%TITLE% MODBUS-RTU.MAC
#%NAME%
# Macro functions to access slave MODBUS devices in RTU mode, which uses
#binary communication as opposed to ASCII in the file modbus.mac.
#
#%DESCRIPTION%
# This macro set provides functions to access remote devices using MODBUS
# protocol over serial lines using the binary (called RTU) mode.%BR%
# %BR%
# The following MODBUS functions are implemented and have been tested with a
# Eurotherm 2408 temperature controller. Further functions will have to be added as
# new instruments come along:
# %UL%%UL%
# %LI% Function code | Function
# %LI% 01 or 02\0\0\0\0\0\0| Read n bits
# %LI% 03 or 04\0\0\0\0\0\0| Read n words
# %LI% 05\0\0\0\0\0\0\0\0\0\0\0\0| Write a bit
# %LI% 06\0\0\0\0\0\0\0\0\0\0\0\0| Write a word
# %LI% 07\0\0\0\0\0\0\0\0\0\0\0\0| Fast Read of Status
# %LI% 08\0\0\0\0\0\0\0\0\0\0\0\0| Loopback
# %LI% 16\0\0\0\0\0\0\0\0\0\0\0\0| Write n words
# %XUL%%XUL%
# The remote devices must behave as MODBUS slave nodes and have to be
# register with the %B%modb_rtu_addnode()%B% macro function before being
# accessed. This function returns a node number that must be used as the
# first parameter of the all the macro functions used to exchange data with
# the remote devices.%BR%
# Many of the input/output functions use an array to transfer data. This
# array can be a data array or an associative array. In the case of using
# a data array, the user is responsible of providing an array of the correct
# size to hold the data.%BR%
# %BR%
# When an error happens during the execution of a MODBUS function, the
# corresponding error is stored in the global variable MODB_ERR if it
# exists and the macro function returns the negative value -MODB_ERR. An error
# message is printed on the output terminal unless MODB_ERR is previously set
# to -1. If the function completes succesfully, MODB_ERR is set to 0.%BR%
# %BR%
# The error codes are the following:
# %UL%%UL%
# %LI% 1001 - Bad device or interface
# %LI% 1002 - Bad address
# %LI% 1003 - Bad data parameters
# %LI% 1004 - Not a registered slave node
# %LI% 1005 - No answer from slave node
# %LI% 1006 - Bad answer from slave node
# %LI% 1007 - Function mismatch
#%EXAMPLE%
#
#%B%myslave\0\0\0=\0modb_rtu_addnode(3,\047)%B%%BR%
#%B%modb_rtu_read_status(myslave,\0data)%B%%BR%
# This reads the status of the slave with
# MODBUS address 47 that is connected to the %B%spec%B% serial
# interface 3.%BR%
#$Revision: 1.5 $, $Date: 2009/03/11 13:06:06 $
#%END%
#%UU% (<device>, <maddr> [, <name>])
#%MDESC%
# Tries to connect to a MODBUS slave node. If the node is found, the macro
# function appends it to the internal list and returns a unique node number
# that must be used to access the slave with this macro set.%BR%
#
# The parameter <device> must be either the number of a serial line interface
# or an ESRF device name. The parameter <maddr> is required and must
# correspond to the MODBUS address of the node.%BR%
#
# If there is an error, the macro returns -MODB_ERR, and the node is not
# added to the internal list.%BR%
#
def modb_rtu_addnode(device, maddr, name) '{
global MODBUS[]
local comm answ data[] node
if (maddr < 0 || maddr > 255) {
if (MODB_ERR != -1)
printf(" MODBUS: Bad address (%d).\n", address)
return(-(MODB_ERR = 1002))
}
if (device + 0 == device) {
comm = "serial"
answ = ser_par(device, "timeout")
} else if (index(device, "/") != 0) {
comm = "esrf"
answ = esrf_io(device, "DevState")
}
if (answ < 0) {
if (MODB_ERR != -1)
printf(" MODBUS: Bad device or interface (%s).\n", device)
return(-(MODB_ERR = 1001))
}
MODBUS[0]["comm"] = comm
MODBUS[0]["dev"] = device
MODBUS[0]["addr"] = maddr
answ = modb_rtu_read_diagnostics(0, 1, 0xff00, data)
if ( answ < 0 ) {
return(-MODB_ERR)
}
for (node = 1; node <= MODBUS[0]["n_nodes"]; node++) {
if ((name != "" && MODBUS[node] == name) || \
(MODBUS[node]["dev"] == device )) {
if (MODBUS[node] == "") {
printf("Replacing unamed MODBUS node by %s:%d\n", device, maddr)
name = device ":" maddr
} else if (MODBUS[node] != name) {
printf("Replacing MODBUS node \'%s\' by \'%s\' at %s:%d\n", \
MODBUS[node], name, device, maddr)
}
break;
}
}
if (node > MODBUS[0]["n_nodes"])
MODBUS[0]["n_nodes"] = node
MODBUS[node] = name
MODBUS[node]["comm"] = comm
MODBUS[node]["dev"] = device
MODBUS[node]["addr"] = maddr
MODB_ERR = 0
return(node)
} '
#%UU% (<device> [, <name>])
#%MDESC%
# Tries to find a MODBUS slave node. The macro will run through the MODBUS
# addresses starting with 1 going to 255. By the nature of things, higher
# addresses will take longer to be found. Once the node is found, the macro
# function appends it to the internal list and returns a unique node number
# that must be used to access the slave with this macro set.%BR%
#
# The parameter <device> must be either the number of a serial line interface
# or an ESRF device name. %BR%
#
# If there is an error, the macro returns -MODB_ERR, and the node is not
# added to the internal list.%BR%
#
def modb_rtu_searchnode(device, name, opts) '{
global MODBUS[]
local comm answ data[] node maxn maddr
if (device + 0 == device) {
comm = "serial"
answ = ser_par(device, "timeout")
} else if (index(device, "/") != 0) {
comm = "esrf"
answ = esrf_io(device, "DevState")
}
if (answ < 0) {
if (MODB_ERR != -1)
printf(" MODBUS: Bad device or interface (%s).\n", device)
return(-(MODB_ERR = 1001))
}
MODBUS[0]["comm"] = comm
MODBUS[0]["dev"] = device
maxn = opts ? opts : 255
for (maddr = 1; maddr < maxn; maddr++) {
MODBUS[0]["addr"] = maddr
if (modb_rtu_read_diagnostics(0, 1, 0xff00, data) == 0xff00) {
break
} else {
tty_cntl("updated?"); tty_cntl("ce"); printf("\r")
}
}
if ( node > maxn ) {
return(-(MODB_ERR = 1005))
}
for (node = 1; node <= MODBUS[0]["n_nodes"]; node++) {
if ((name != "" && MODBUS[node] == name) || \
(MODBUS[node]["dev"] == device )) {
if (MODBUS[node] == "") {
printf("Replacing unamed MODBUS node by %s:%d\n", device, maddr)
name = device ":" maddr
} else if (MODBUS[node] != name) {
printf("Replacing MODBUS node \'%s\' by \'%s\' at %s:%d\n", \
MODBUS[node], name, device, maddr)
}
break;
}
}
if (node > MODBUS[0]["n_nodes"])
MODBUS[0]["n_nodes"] = node
MODBUS[node] = name
MODBUS[node]["comm"] = comm
MODBUS[node]["dev"] = device
MODBUS[node]["addr"] = maddr
MODB_ERR = 0
return(node)
} '
#%UU% (<device>)
#%MDESC%
# Tries to work out the serial line parameters to communicate with a MODBUS
# slave node. The macro will run through the possible serial line parameters,
# which are 9600, 19200, 4800, 2400 and 1200 baud. At each speed, it will try
# to communicate at 8 data bits, then 7 data bits with even, then odd parity.
# By the nature of things, things are going to be slow. Once a communication is
# established, the macro will offer the result in printing.
# %BR%
# The parameter <device> must be either the number of a serial line interface
# or an ESRF device name.
def modb_rtu_searchparameters(device, maddr, opts) '{
global MODBUS[]
local comm answ data[] node bla, i, speeds, aux[], databs
local serpar[], j, k, comdev, databs
if (device + 0 == device) {
comm = "serial"
answ = ser_par(device, "timeout")
} else if (index(device, "/") != 0) {
comm = "esrf"
answ = esrf_io(device, "DevState")
}
if (answ < 0) {
if (MODB_ERR != -1)
printf(" MODBUS: Bad device or interface (%s).\n", device)
return(-(MODB_ERR = 1001))
}
MODBUS[0]["comm"] = comm
MODBUS[0]["dev"] = device
MODBUS[0]["addr"] = maddr
# in order to use an esrf_io call, we need the device_id
if (comm == "serial") {
comdev = ser_par(device, "device_id")
} else {
comdev = device
}
SL_NONE = 0; SL_ODD = 1; SL_EVEN = 3
local Parity[]
Parity[SL_NONE] = ""
Parity[SL_ODD ] = ", odd parity"
Parity[SL_EVEN] = ", even parity"
SL_DATA8 = 0; SL_DATA7 = 1
local DataBits[]
DataBits[SL_DATA8] = 8
DataBits[SL_DATA7] = 7
local parmode[]
parmode[j++] = SL_NONE
parmode[j++] = SL_ODD
parmode[j++] = SL_EVEN
serpar[0] = 4; serpar[1] = SL_NONE # No parity
serpar[2] = 5; serpar[3] = SL_DATA8 # 8 data bits
serpar[4] = 7; serpar[5] = 9600 # baud rate
bla = "19200 9600 4800 2400 1200"
speeds = split(bla, aux)
def blabla \'{
local x, str
if (esrf_io(comdev, "DevSerSetParameter", serpar) < 0)
return(-(MODB_ERR = 1001))
str = sprintf("%5d baud, %d data bits", serpar[5], serpar[3])
str = str sprintf("%-14s", Parity[serpar[1]])
x = modb_rtu_read_diagnostics(0, 1, 0xff00, data)
# clear error message
if (x < 0) {
tty_cntl("updated?"); tty_cntl("ce"); printf("\r")
str = str " .... No!"
} else {
str = str " .... Yes! ******"
}
print str
# one could try to send a BI-SYNC command "II" here.
} \'
print "Serial line config: "
for (i = 0; i < speeds; i++) {
serpar[5] = aux[i]
for (j = 0; j < 2; j++) {
serpar[3] = j
if (j) {
for (k = 0; k < 3; k ++) {
serpar[1] = parmode[k]
ESRF_ERR = -1
blabla
}
} else {
serpar[1] = SL_NONE
blabla
}
}
}
undef blabla
} '
#%UU% (<node>, <data>)
#%MDESC%
# This macro executes the function 7 "FAST READ OF STATUS" in a slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_rtu_addnode()%B%.
# The 8-bit status is returned in the first element of the array <data>.%BR%
# If an error happens, the macro returns -MODB_ERR (see error codes),
# otherwise 1.%BR%
#
def modb_rtu_read_status(node, data, opts) '{
local err
modb_rtu__debug_start
if (!(opts & 0x02)) {
if (err = modb_rtu__frame(node, 7))
return(err)
if (err = modb_rtu__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb_rtu__get(node, 7))
return(err)
data[0] = MODBFRAME[2]
MODB_ERR = 0
modb_rtu__debug "end"
return(MODBFRAME[2])
}
MODB_ERR = 0
modb_rtu__debug "end"
return(0)
}'
#%UU% (<node>, <subcode>, <dfield>, <data>)
#%MDESC%
# This macro executes the function 8 "DIAGNOSTIC LOOPBACK" in a
# slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_rtu_addnode()%B%.
# If no error happens, the macro function returns the number of words actually
# read from the slave and stored in the first positions of the array <data>.
# Each valid position of <data> contains a 16-bit word.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_rtu_read_diagnostics(node, subcode, dfield, data, opts) '{
modb_rtu__debug_start
return(modb_rtu__read(node, subcode, dfield, data, opts, 8))
}'
##%UU% (<node>, <function>, <data>)
##%MDESC%
## This macro executes a function of the user's choice in a slave MODBUS node.
#
## The slave node is selected by the parameter <node> that must be a valid
## value previously returned by the macro function %B%modb_rtu_addnode()%B%.
#
## If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
##
#def modb_rtu_function(node, function, opts) '{
# local err
# modb_rtu__debug_start
# if (!(opts & 0x02)) {
# if (err = modb_rtu__frame(node, 17))
# return(err)
# if (err = modb_rtu__put(node))
# return(err)
# }
# if (!(opts & 0x01)) {
# if (err = modb_rtu__get(node, 17))
# return(err)
# MODB_ERR = 0
# modb_rtu__debug "end"
# return(MODBFRAME[2])
# }
# MODB_ERR = 0
# modb_rtu__debug "end"
# return(0)
#}'
#%UU% (<node>, <address of first bit>, <number of bits>, <data>)
#%MDESC%
# This macro executes the function 1 or 2 "Read n bits" in a slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_rtu_addnode()%B%.
# If no error happens, the macro function returns the number of bits actually
# read from the slave and stored in the first positions of the array <data>.
# Each valid position of <data> contains ONE bit of the answer.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_rtu_read_n_bits(node, fbaddr, nbits, data, opts) '{
local err, funct
funct = 1
modb_rtu__debug_start
modb_rtu__debug "modb_rtu_read_n_bits"
if (err = modb_rtu__checkpar(nbits, data))
return(err)
if (!(opts & 0x02)) {
if (err = modb_rtu__frame(node, funct, fbaddr, nbits, data))
return(err)
if (err = modb_rtu__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb_rtu__get(node, funct))
return(err)
modb_rtu__data(data, nbits)
MODB_ERR = 0
modb_rtu__debug "end"
return(nbits)
}
MODB_ERR = 0
modb_rtu__debug "end"
return(0)
} '
#%UU% (<node>, <address of bit>, <value of bit>, <data>)
#%MDESC%
# This macro executes the function 5 "write a bit" in a slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_rtu_addnode()%B%.
# If no error happens, the macro function returns 1
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_rtu_write_a_bit(node, fbaddr, value, data, opts) '{
local err, funct
funct = 5
modb_rtu__debug_start
modb_rtu__debug "modb_rtu_write_a_bit"
if (!(opts & 0x02)) {
if (err = modb_rtu__frame(node, funct, fbaddr, value, data))
return(err)
if (err = modb_rtu__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb_rtu__get(node, funct))
return(err)
modb_rtu__data(data, value)
MODB_ERR = 0
modb_rtu__debug "end"
return(1)
}
MODB_ERR = 0
modb_rtu__debug "end"
return(0)
} '
#%UU% (<node>, <address of first word>, <number of words>, <data>)
#%MDESC%
# This macro executes the function 3 or 4 "Read n words" in a slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_rtu_addnode()%B%.
# If no error happens, the macro function returns the number of words actually
# read from the slave and stored in the first positions of the array <data>.
# Each valid position of <data> contains ONE bit of the answer.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_rtu_read_n_words(node, fwaddr, nwords, data, opts) '{
local err, funct
funct = 3
modb_rtu__debug_start
modb_rtu__debug "modb_rtu_read_n_words"
if (err = modb_rtu__checkpar(nwords, data))
return(err)
if (!(opts & 0x02)) {
if (err = modb_rtu__frame(node, funct, fwaddr, nwords, data))
return(err)
if (err = modb_rtu__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb_rtu__get(node, funct))
return(err)
modb_rtu__data(data, nwords)
MODB_ERR = 0
modb_rtu__debug "end"
return(nwords)
}
MODB_ERR = 0
modb_rtu__debug "end"
return(0)
} '
#%UU% (<node>, <address of word>, <value of word>, <data>)
#%MDESC%
# This macro executes the function 6 "Write a word" in a slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_rtu_addnode()%B%.
# If no error happens, the macro function returns 1.
# Each valid position of <data> contains ONE bit of the answer.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_rtu_write_a_word(node, fwaddr, value, opts) '{
local err, funct, data[]
funct = 6
modb_rtu__debug_start
modb_rtu__debug "modb_rtu_write_a_word"
if (!(opts & 0x02)) {
if (err = modb_rtu__frame(node, funct, fwaddr, value, data))
return(err)
if (err = modb_rtu__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb_rtu__get(node, funct))
return(err)
# modb_rtu__data(data, value)
MODB_ERR = 0
modb_rtu__debug "end"
return(nwords)
}
MODB_ERR = 0
modb_rtu__debug "end"
return(0)
} '
#%UU% (<node>, <address of first word>, <number of words>, <data>)
#%MDESC%
# This macro executes the function 16 "WRITE N WORDS" in a slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_rtu_addnode()%B%.
# If no error happens, the macro function returns the number of words actually
# written to the slave.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_rtu_write_n_words(node, fwaddr, nwords, data, opts) '{
local err, funct
funct = 16
modb_rtu__debug_start
modb_rtu__debug "modb_rtu_write_n_words"
if (err = modb_rtu__checkpar(nwords, data))
return(err)
if (!(opts & 0x02)) {
if (err = modb_rtu__frame(node, funct, fwaddr, nwords, data))
return(err)
if (err = modb_rtu__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb_rtu__get(node, funct))
return(err)
modb_rtu__data(data, nwords)
MODB_ERR = 0
modb_rtu__debug "end"
return(nwords)
}
MODB_ERR = 0
modb_rtu__debug "end"
return(0)
} '
#%IU% (<node>, <daddr>, <nitems>, <data>, <function>)
#%MDESC%
# Executes any read data function in a slave MODBUS node.%BR%
#
def modb_rtu__read(node, daddr, nitems, data, opts, funct) '{
local err
modb_rtu__debug "modb_rtu__read"
if (err = modb_rtu__checkpar(nitems, data))
return(err)
if (!(opts & 0x02)) {
if (err = modb_rtu__frame(node, funct, daddr, nitems))
return(err)
if (err = modb_rtu__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb_rtu__get(node, funct))
return(err)
modb_rtu__data(data, nitems)
MODB_ERR = 0
modb_rtu__debug "end"
return(nitems)
}
MODB_ERR = 0
modb_rtu__debug "end"
return(0)
}'
#%IU% (<data>, <nitems>)
#%MDESC%
# Extracts <nitems> from the binary frame in MODBFRAME and stores them
# as %B%float%B% %B%values%B% in the array <data>.%BR%Tested only for function
# modb_rtu_read_n_words(). %BR%
# The 2408 can be put into a floating point mode (read cell 12550) which then
# displays values with variable number of floating point numbers. To avoid
# getting in trouble with that, the protocol allows to read 4 bytes from
# adresses over 0x8000. Thus, the cell 2 reads from %BR%
# 2 x 2 + 8000h = 8004h = 32772 decimal. %BR%
def modb_rtu__float(data, nitems) '{
local funct i j aux
local s0 s1 s2 s3
modb_rtu__debug "modb_rtu__float"
funct = MODBFRAME[1]
j = 3 # where the answer starts
if (funct == 3) {
local f1, x, e, s[]
for (i = 0; i < nitems; i++) {
s[0] = MODBFRAME[j++]
s[1] = MODBFRAME[j++]
s[2] = MODBFRAME[j++]
s[3] = MODBFRAME[j]
f1 = 0x800000 | (s[1]&0x7f) << 16 | s[2] << 8 | s[3]
e = ((s[0]&0x7F)<<1) | ((s[1]&0x80)>>7)
e -= 127
x = f1 * pow(2., -23. + e)
if (s[0]&0x80)
x = -x
data[i] = x
}
}
} '
#%IU% (<data>, <nitems>)
#%MDESC%
# Extracts <nitems> data from the binary frame in MODBFRAME and stores them
# in the array <data>.%BR%
#
def modb_rtu__data(data, nitems) '{
local funct i j aux
modb_rtu__debug "modb_rtu__data"
funct = MODBFRAME[1]
j = 3 # where the answer starts
if (funct == 1 || funct == 2) {
for (i = 0; i < nitems; i++) {
if ((i % 8) == 0) {
aux = MODBFRAME[j++]
}
data[i] = aux & 0x01
aux >>= 1
}
} else if (funct == 3 || funct == 4) {
for (i = 0; i < nitems; i++) {
aux = MODBFRAME[j++] << 8
aux |= MODBFRAME[j++]
data[i] = aux
}
} else if (funct == 5) {
data[0] = MODBFRAME[4]
} else if (funct == 6) {
aux = MODBFRAME[4] << 8
aux |= MODBFRAME[5]
data[0] = aux
}
} '
#%IU% (<nitems>, <data>)
#%MDESC%
# Checks that nitems is in the (1, 0xFFFF) range and that data is an
# array of the right capacity.%BR%
#
def modb_rtu__checkpar(nitems, data) '{
modb_rtu__debug "modb_rtu__checkpar"
if (MODBFRAME[1] != 8)
if (nitems <= 0 || nitems > 0xFFFF || nitems != int(nitems)) {
if (MODB_ERR != -1)
print " MODBUS: Bad data parameters (number of items)"
return(-(MODB_ERR = 1003))
}
if (!(whatis("data") & 0x01010000)) {
if (MODB_ERR != -1)
print " MODBUS: Bad data parameters (not an array)"
return(-(MODB_ERR = 1003))
}
return(0)
}'
#%IU% (<node>, <funct>, <word1>, <word2>, <data>)
#%MDESC%
# Builds a modbus frame from the input parameters <funct>, <word1>, <word2>
# and <data>. The frame is stored in the global byte array MODBFRAME[]
# starting from the position 1.%BR%
#
def modb_rtu__frame(node, funct, word1, word2, data) '{
local addr i j byt shft, len
local ubyte array CRC[2]
j = 0
modb_rtu__debug "modb_rtu__frame - function", funct
addr = MODBUS[node]["addr"]
if (addr < 0) {
if (MODB_ERR != -1)
print " MODBUS: Not a valid slave node:", node
return(-(MODB_ERR = 1004))
}
if (funct == 7) {
len = 2
global ubyte array MODBFRAME[len + 2]
MODBFRAME[0] = addr
MODBFRAME[1] = funct
MODBUS["anslen"] = 3
CRC = modb_rtu_CRC(MODBFRAME)
MODBFRAME[len] = CRC[1]
MODBFRAME[len + 1] = CRC[0]
return(0)
} else if (funct == 8) {
len = 6
global ubyte array MODBFRAME[len + 2]
MODBFRAME[0] = addr
MODBFRAME[1] = funct
MODBFRAME[2] = word1 >> 8
MODBFRAME[3] = word1 & 0xFF
MODBFRAME[4] = word2 >> 8
MODBFRAME[5] = word2 & 0xFF
CRC = modb_rtu_CRC(MODBFRAME)
MODBUS["anslen"] = 6
MODBFRAME[len] = CRC[1]
MODBFRAME[len + 1] = CRC[0]
return(0)
} else if (funct == 1 || funct == 2) {
len = 6
global ubyte array MODBFRAME[len + 2]
MODBFRAME[0] = addr
MODBFRAME[1] = funct
MODBFRAME[2] = word1 >> 8
MODBFRAME[3] = word1 & 0xFF
MODBFRAME[4] = word2 >> 8
MODBFRAME[5] = word2 & 0xFF
CRC = modb_rtu_CRC(MODBFRAME)
MODBFRAME[len] = CRC[1]
MODBFRAME[len + 1] = CRC[0]
MODBUS["anslen"] = 4 + int((word2 - 1) / 8)
return(0)
} else if (funct == 3 || funct == 4) {
len = 6
global ubyte array MODBFRAME[len + 2]
MODBFRAME[0] = addr
MODBFRAME[1] = funct
MODBFRAME[2] = word1 >> 8
MODBFRAME[3] = word1 & 0xFF
MODBFRAME[4] = word2 >> 8
MODBFRAME[5] = word2 & 0xFF
if (word2 > 125) {
eprint "too many words to read"
exit
}
CRC = modb_rtu_CRC(MODBFRAME)
MODBFRAME[len] = CRC[1]
MODBFRAME[len + 1] = CRC[0]
MODBUS["anslen"] = 3 + word2 * 2
return(0)
} else if (funct == 5) {
len = 6
global ubyte array MODBFRAME[len + 2]
MODBFRAME[j++] = addr
MODBFRAME[j++] = funct
MODBFRAME[j++] = word1 >> 8
MODBFRAME[j++] = word1 & 0xFF
MODBFRAME[j++] = word2 & 0xFF
MODBFRAME[j++] = 0
CRC = modb_rtu_CRC(MODBFRAME)
MODBFRAME[j++] = CRC[1]
MODBFRAME[j++] = CRC[0]
MODBUS["anslen"] = 3
return(0)
} else if (funct == 6) {
len = 6
global ubyte array MODBFRAME[len + 2]
MODBFRAME[j++] = addr
MODBFRAME[j++] = funct
MODBFRAME[j++] = word1 >> 8
MODBFRAME[j++] = word1 & 0xFF
MODBFRAME[j++] = word2 >> 8
MODBFRAME[j++] = word2 & 0xFF
CRC = modb_rtu_CRC(MODBFRAME)
MODBFRAME[j++] = CRC[1]
MODBFRAME[j++] = CRC[0]
MODBUS["anslen"] = 6
return(0)
} else if (funct == 16) {
if (word2 > 125) {
eprint "too many words to write"
exit
}
len = 7 + 2 * word2
global ubyte array MODBFRAME[len + 2]
MODBFRAME[j++] = addr
MODBFRAME[j++] = funct
MODBFRAME[j++] = word1 >> 8
MODBFRAME[j++] = word1 & 0xFF
MODBFRAME[j++] = word2 >> 8
MODBFRAME[j++] = word2 & 0xFF
MODBFRAME[j++] = (word2 & 0xFF) * 2
for (i = 0; i < word2; i++) {
MODBFRAME[j++] = data[i] >> 8
MODBFRAME[j++] = data[i] & 0xFF
}
CRC = modb_rtu_CRC(MODBFRAME)
MODBFRAME[j++] = CRC[1]
MODBFRAME[j++] = CRC[0]
MODBUS["anslen"] = 6
return(0)
# } else if (funct == 17) {
# len = 2
# global ubyte array MODBFRAME[len + 2]
# MODBFRAME[j++] = addr
# MODBFRAME[j++] = funct
# CRC = modb_rtu_CRC(MODBFRAME)
# MODBFRAME[j++] = CRC[1]
# MODBFRAME[j++] = CRC[0]
# MODBUS["anslen"] = 6
# return(0)
}
return(-(MODB_ERR = 99))
}'
#%UU%(len)
#%MDESC%
# This macro calculates the CRC over the present MODBFRAME. Lenght is the argument.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_rtu_CRC(arr) '{
#/* CRC runs cyclic Redundancy Check Algorithm on input z_p */
#/* Returns value of 16 bit CRC after completion and */
#/* always adds 2 crc bytes to message */
#/* returns 0 if incoming message has correct CRC */
local ubyte array CRC[2]
local lcrc, next, carry, n crch, crcl, i, len
CRC[:]= 0xff
lcrc = 0xffff
len = array_op("cols", arr) - 2
modb_rtu__debug "modb_rtu_CRC"
for (i = 0; i < len; i++) {
next = arr[i]
lcrc ^= next;
for (n = 0; n < 8; n++) {
carry = lcrc & 1;
lcrc >>= 1;
if (carry) {
lcrc ^= 0xA001;
}
}
}
modb_rtu__debug "modb_rtu_CRC", sprintf("%04x", lcrc)
CRC[0] = lcrc / 256;
CRC[1] = lcrc % 256
MODB_ERR = 0
return(CRC)
} '
#%IU% (<node>)
#%MDESC%
# Sends a message to a slave node and gets the answer back.
# The message is constructed from a valid modbus frame previously stored in
# the byte array MODBFRAME[]. The answer from the slave is stored also in
# MODBFRAME[], overwritting the initial content. The return value is the
# length of the answer frame or -MODB_ERR in case of error.%BR%
#
def modb_rtu__put(node) '{
local comm device
modb_rtu__debug "modb_rtu__put address:", (MODBFRAME[2] <<8) + MODBFRAME[3]
comm = MODBUS[node]["comm"]
device = MODBUS[node]["dev"]
if (comm == "serial") {
ser_par(device, "flush", 2)
if (ser_put(device, MODBFRAME) == -1) {
if (MODB_ERR != -1)
print " MODBUS: Transmission error (ser_put)"
return(-(MODB_ERR = 1008))
}
} else {
local len, str, i
len = array_op("cols", MODBFRAME)
ESRF_ERR = -1
for (i = 0; i < len; i ++) {
if (esrf_io(device, "DevSerWriteChar", MODBFRAME[i]) == -1) {
if (MODB_ERR != -1)
print " MODBUS: Transmission error (esrf_io)"
return(-(MODB_ERR = 1008))
}
}
}
modb_rtu__debug "modb_rtu__put", MODBFRAME
MODBUS[node]["funct"] = MODBFRAME[1]
return(0)
}'
def modb_rtu__get(node, funct) '{
local retval
modb_rtu__debug "modb_rtu__get"
if (funct != MODBUS[node]["funct"]) {
if (MODB_ERR != -1)
print " MODBUS: Function mismatch"
return(-(MODB_ERR = 1007))
}
retval = modb_rtu__getlow(node)
if (!retval)
retval = modb_rtu__chkanswer(funct)
if (retval == -1007) {
print "Retry: ", modb_rtu__getlow(node)
}
return(retval)
}'
def modb_rtu__getlow(node) '{
local comm device i
comm = MODBUS[node]["comm"]
device = MODBUS[node]["dev"]
local len, gotten
len = MODBUS["anslen"] # is the lenght of the string sent, wo CRC
local ubyte array __mb_str[len + 2]
local ubyte array __error[5]
modb_rtu__debug "modb_rtu__getlow len=" len + 2
# in order to capture an error, we have to catch the function byte, which
# in case of an error, will have the most significant bit set! Then get the
# rest of the answer.
#
# After non conclusive tests with multiple spec sessions reading from the same
# serial line, the read needs to be done in ONE go. Otherwise a different spec
# session might jump in and catch the answer of this one. So read all the
# bytes expected and worry about errors later.
if (comm == "serial") {
local i
gotten = ser_get(device, __mb_str)
# of course, if the error bit is set, we are going to get a time out,
# but we will have to live with that, I guess.
if (__mb_str[1] & 128) { # this is a modbus data error
__error = __mb_str[0:4]
# now check the error code (in byte 3)
if (__error[2] == 3) {
eprint "Modbus data error: Illegal Data Value"
eprint " The value referenced in the data field is not allowable in the"
eprint " addressed slave location", (MODBFRAME[2] << 8) + MODBFRAME[3]
return(-(MODB_ERR = 1003))
}
else if (__error[2] == 2) {
eprint "Modbus data error: Illegal Data Address"
eprint " The address referenced in the data field is not an"
eprint " allowable address for the slave"
return(-(MODB_ERR = 1003))
}
else {
eprint "Modbus data error: Unknown error", __error[2] "!"
return(-(MODB_ERR = 1003))
}
}
if (!gotten) {
gotten = ser_get(device, __mb_str[0:3])
}
modb_rtu__debug "modb_rtu__getlow gotten", gotten, "chars"
if (__mb_str[1] & 128) { # this is a modbus data error
__error[0:1] = __mb_str[2:3]
# now check the error code (in byte 3)
if (__error[0] == 3) {
eprint "Modbus data error: Illegal Data Value"
eprint " The value referenced in the data field is not allowable in the"
eprint " addressed slave location"
return(-(MODB_ERR = 1003))
}
else if (__error[0] == 2) {
eprint "Modbus data error: Illegal Data Address"
eprint " The address referenced in the data field is not an"
eprint " allowable address for the slave"
return(-(MODB_ERR = 1003))
}
else {
eprint "Modbus data error: Unknown error", __error[0] "!"
return(-(MODB_ERR = 1003))
}
}
if (gotten == 0) {
if (MODB_ERR != -1)
print " MODBUS: No answer from slave node"
return(-(MODB_ERR = 1005))
}
} else {
ESRF_ERR = -1
gotten = esrf_io(device, "DevSerReadChar", (len << 8) | 1, __mb_str)
if (ESRF_ERR){
if (MODB_ERR != -1)
print " MODBUS: No answer from slave node"
return(-(MODB_ERR = 1005))
}
if (__mb_str[1] & 128) { # this is a modbus data error
__error[0:1] = __mb_str[0:4]
# now check the error code (in byte 3)
if (__error[2] == 3) {
eprint "Modbus data error: Illegal Data Value"
eprint " The value referenced in the data field is not allowable in the"
eprint " addressed slave location"
return(-(MODB_ERR = 1003))
}
else if (__error[2] == 2) {
eprint "Modbus data error: Illegal Data Address"
eprint " The address referenced in the data field is not an"
eprint " allowable address for the slave"
return(-(MODB_ERR = 1003))
}
else {
eprint "Modbus data error: Unknown error", __error[2] "!"
return(-(MODB_ERR = 1003))
}
}
}
modb_rtu__debug "modb_rtu__getlow: received", __mb_str
MODBUS[node]["funct"] = 0
if (err = modb_rtu__chkstr(__mb_str))
return(err)
# now recreate MODBFRAME in the right length and copy __mb_str into it.
global ubyte array MODBFRAME[len + 2]
MODBFRAME = __mb_str
return(0)
}'
def __modb_rtu__getdata(node, funct) '{
local comm device str len
modb_rtu__debug "__modb_rtu__getdata"
modb_rtu__debug funct
comm = MODBUS[node]["comm"]
device = MODBUS[node]["dev"]
if (funct != MODBUS[node]["funct"]) {
if (MODB_ERR != -1)
print " MODBUS: Function mismatch"
return(-(MODB_ERR = 1007))
}
if (comm == "serial") {
str = ser_get(device)
if (str == "") {
if (MODB_ERR != -1)
print " MODBUS: No answer from slave node"
return(-(MODB_ERR = 1005))
}
} else {
ESRF_ERR = -1
str = esrf_io(device, "DevSerReadString", 2)
if (ESRF_ERR){
if (MODB_ERR != -1)
print " MODBUS: No answer from slave node"
return(-(MODB_ERR = 1005))
}
}
MODBUS[node]["funct"] = 0
if (err = modb_rtu__chkstr(str))
return(err)
return(modb_rtu__chkanswer(funct))
}'
#%IU% (<str>)
#%MDESC%
# Store the answer from the slave into MODBFRAME[].%BR%
# If the macro detects a MODBUS exception or a CRC error returns -MODB_ERR,
# otherwise returns the length of the answer frame.%BR%
#
def modb_rtu__chkstr(str) '{
local i len hd ld
local ubyte array CRC[2]
modb_rtu__debug "modb_rtu__chkstr"
# check the two first bytes
if ((str[0] != MODBFRAME[0]) && (str[1] != MODBFRAME[1])) {
print " MODBUS: Bad answer (first two bytes not identical)."
print str, MODBFRAME
return(-(MODB_ERR = 1006))
}
len = MODBUS["anslen"] # is the lenght of the string sent, wo CRC
CRC = modb_rtu_CRC(str)
if ((CRC[0] != str[len+1]) || (CRC[1] != str[len])) {
print " MODBUS: Bad answer (CRC error)."
return(-(MODB_ERR = 1006))
}
return(0)
} '
#%IU% ()
#%MDESC%
# Checks the received frame for a MODBUS exception and returns the error code.
# If no exception, it returns the length of the answer frame.%BR%
#
def modb_rtu__chkanswer(funct) '{
modb_rtu__debug "modb_rtu__chkanswer"
if (MODBFRAME[1] != funct) {
if (MODB_ERR != -1)
print " MODBUS: Function mismatch"
return(-(MODB_ERR = 1007))
}
if (MODBFRAME[1] & 0x80) {
MODB_ERR = MODBFRAME[2]
if (MODB_ERR != -1) {
if (MODB_ERR == 1)
printf(" MODBUS: Illegal Function.\n")
else if (MODB_ERR == 2)
printf(" MODBUS: Illegal Data Address.\n")
else if (MODB_ERR == 3)
printf(" MODBUS: Illegal Data Value.\n")
else {
printf(" MODBUS: Unknown Exception (%d).\n", MODB_ERR)
}
}
return(-MODB_ERR)
}
MODB_ERR = 0
return(0)
}'
#%UU%
#%MDESC%
def modb_rtu_debug '{
if (MODBUS[0]["debug"]) {
MODBUS[0]["debug"] = 0
unglobal MODBDEBUGT
rdef modb_rtu__debug_start ""
rdef modb_rtu__debug "0==\$6"
print "MODBUS debug mode is OFF"
} else {
MODBUS[0]["debug"] = 1
global MODBDEBUGT
rdef modb_rtu__debug_start \'MODBDEBUGT = time()\'
rdef modb_rtu__debug \'print printf("%7.2f", 1000*(time() - MODBDEBUGT)), "$*" \'
print "MODBUS debug mode is ON"
}
}'
if (!(whatis("modb_rtu__debug_start")&2)) rdef modb_rtu__debug_start ''
if (!(whatis("modb_rtu__debug")&2)) rdef modb_rtu__debug '0==$6'
#%MACROS%
#%IMACROS%
#%AUTHOR% P. Fajardo, (Original 5/01). RTU version based on PF
# modbus.mac: H. Witsch March 2008
# $Revision: 1.5 $ / $Date: 2009/03/11 13:06:06 $
#%TOC%
|