MODBUS
ModBus App Notes

Modbus protocol for CRC calculation and response events -v49.03.

Added MODBUS protocol to handle CRC calculation and response events.

 
Modbus RTU is available on RS2, RS4, AS1, AS2 - v49.32.
More than one of these can be active at the same time if required.

Setup example:
SETUP(RS2)
{
baud = 19200;
parity = N;
rxi = Y;
txi = Y;
proto = ModbusRTU;   //define modbus as the protocol to use
}

All low level handling of Modbus requests and responses are handled by the TFT firmware and the 'interface' to the itronSMART code is via system variables (xxx refers to the interface name: RS2, RS4, AS1 or AS2):
Name Type Use
MB_xxx_MODE U8 Modbus mode (0=master, 1=slave)
MB_xxx_REGS PTR Pointer to a U16 array to hold read / write registers
MB_xxx_COILS PTR Pointer to a U8 array to hold read / write coil bit values
MB_xxx_SLAVEID U8 Slave address
MB_xxx_FUNC U8 Modbus function code (1/3/15/16)
MB_xxx_REGOFF U16 Offset into U16 register array for read / write action
MB_xxx_REGCNT U16 Number of registers to read / write (or have been read / written)
MB_xxx_COILOFF U16 Offset into U16 coil register array for read / write action
MB_xxx_COILCNT U16 Number of coil registers to read / write (or have been read / written)
MB_xxx_STATUS U8 Modbus status (0=idle, 1=tx, 1=rx, 2+ error)
MB_xxx_TIMEOUT U16 Response timeout in milliseconds
MB_xxx_DATAOK PTR Pointer to function that is called after a successful transaction
MB_xxx_DATAERR PTR Pointer to function that is called after an error has occurred

COIL data is converted from bits to bytes to make itronSMART access easier. For example a Modbus request to read 12 coils (2 Modbus data bytes) with offset of 6 will result in bytes 6 – 17 in the U8 coil registers being used.
 
Master Slave
// New modbus test
lib(fnt, "SDHC/asc_32.fnt");
style(ps, page){back = black;}
style(ts, text){font = fnt; col = white; currel = TL;}
style(ts_cc, text){font = fnt; col = white; currel = CC;}

setup(RS2)
{
baud = 19200;
parity = N;
encode = sd;
txi = Y;
rxi = Y;
proto = ModbusRTU;
}

var(regs, 0, U16, 128);

load(MB_RS2_MODE, 0);
load(MB_RS2_REGS > "regs");
load(MB_RS2_SLAVEID, 1);
load(MB_RS2_TIMEOUT, 1000);

var(v1, 10, U16);
var(v2, 30, U16);
var(v3, 80, U16);
var(v4, 0, U16);
var(v5, 0, U16);
var(v6, 0, U16);

int(tim0, TIMER0, clear_status);

page(p, ps)
{
posn(100, 40); text(t1, "WR", ts);
posn(+0, +35); text(t2, "0", ts);
posn(+0, +30); text(t3, "1", ts);
posn(+0, +30); text(t4, "2", ts);

posn(130, 75); text(t5, v1, ts);
posn(+0, +30); text(t6, v2, ts);
posn(+0, +30); text(t7, v3, ts);

posn(340, 40); text(t8, "RD", ts);
posn(+0, +35); text(t9, "0", ts);
posn(+0, +30); text(t10, "1", ts);
posn(+0, +30); text(t11, "2", ts);

posn(370, 75); text(t12, "", ts);
posn(+0, +30); text(t13, "", ts);
posn(+0, +30); text(t14, "", ts);

posn(120, 136); key(k1, write, 240, 272, TOUCH);
posn(360, 136); key(k2, read, 240, 272, TOUCH);

posn(240, 256); text(status, "", ts_cc);
posn(460, 256); text(status_error, "", ts_cc);
}

func(read)
{
load(MB_RS2_FUNC, 3);
load(MB_RS2_REGOFF, 0);
load(MB_RS2_REGCNT, 3);
load(MB_RS2_DATAOK > "read_ok");
load(MB_RS2_DATAERR > "read_error");
load(RS2, 1);
}

func(read_ok)
{
load(v4, regs.0); text(t12, v4);
load(v5, regs.1); text(t13, v5);
load(v6, regs.2); text(t14, v6);;
text(status, "READ OK");;
load(TIMER0, 1500, 1);
}

func(read_error)
{
text(status, "READ ERROR");
text(status_error, MB_RS2_STATUS);;
load(TIMER0, 1500, 1);
}

func(write)
{
load(MB_RS2_FUNC, 16);
load(MB_RS2_REGOFF, 0);
load(MB_RS2_REGCNT, 3);
load(regs.0, v1);
load(regs.1, v2);
load(regs.2, v3);
load(MB_RS2_DATAOK > "write_ok");
load(MB_RS2_DATAERR > "write_error");
load(RS2, 1);
}

func(write_ok)
{
text(status, "WRITE OK");;
load(TIMER0, 1500, 1);
}

func(write_error)
{
text(status, "WRITE ERROR");
text(status_error, MB_RS2_STATUS);;
load(TIMER0, 1500, 1);
}

func(clear_status)
{
text(status, "");;
text(status_error, "");;
}

show(p);
// New modbus test - slave

lib(fnt, "SDHC/asc_32.fnt");

style(ps, page){back = black; }
style(ts, text){font = fnt; col = white; currel = TL;}

setup(RS2)
{
baud = 19200;
parity = N;
encode = sd;
txi = Y;
rxi = Y;
proto = ModbusRTU;
}

var(regs, 0, U16, 128);

load(MB_RS2_MODE, 1);
load(MB_RS2_REGS > "regs");
load(MB_RS2_SLAVEID, 1);
load(MB_RS2_DATAOK > "ok");
load(MB_RS2_DATAERR > "error");

load(regs.0, 10);
load(regs.1, 9102);
load(regs.2, 234);

int(tim0, TIMER0, clear_status);

page(p, ps)
{
posn(240, 50); text(t1, regs.0, ts);
posn(240, 90); text(t2, regs.1, ts);
posn(240, 130); text(t3, regs.2, ts);
posn(240, 256); text(status, "", ts);
}

func(ok)
{
if(MB_RS2_FUNC==16?[text(status, "WRITE OK");;run(update_values);]:[text(status, "READ OK");;]);
load(TIMER0, 1500, 1);
}

func(error)
{
if(MB_RS2_FUNC==16?[text(status, "WRITE ERROR");;]:[text(status, "READ ERROR");;]);
load(TIMER0, 1500, 1);
}

func(update_values)
{
text(t1, regs.0);
text(t2, regs.1);
text(t3, regs.2);;
}

func(clear_status)
{
text(status, "");;
}

show(p);
 

Master operation
Write regs / coils
Load values into U16 / U8 variable that MB_xxx_REGS/MB_xxx_COILS points to.
Setup MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_xxx_COILOFF and use command LOAD(RS2, 1); to start request.
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the success response is received from the slave.
Read regs / coils
Setup MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_xxx_COILOFF and use command LOAD(RS2, 1); to start request.
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the success response is received from the slave.
Values can then be read from variable that MB_xxx_REGS/MB_xxx_COILS points to.

Slave operation
Write regs / coils
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the successful request from the master.
Values can then be read from variable that MB_xxx_REGS/MB_xxx_COILS points to.
MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_xxx_COILOFF values should be read to know which registers were updated.
Read regs / coils
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the successful request from the master.
MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_COILOFF values can be read to know which registers were read.

 

Update Information

 Version

 Title

Date  

 Details

49.58

 MODBUS

07 Sep 15 

Show

* Fixed a problem that prevented a new transaction from being initiated from within the 'pass' or 'fail' functions

49.37

 Modbus - Additional Functionality

10 Jun 13 

Show

* Added support for function codes 5/6 (write single coil / register).
* Added alternative means of accessing register / coil data using 2 dimensional array. 2 dim mode is automatically selected if MB_xxx_REGS / MB_xxx_COILS points to a 2 dim array. In this case the first dimension specified the address and the second holds the data. The coil array must be U16 in 2 dim mode. 2 dim mode is not fully tested.

49.34

 Modbus - Removed debug messages for RS485 Modbus TX and RX.

23 Feb 13 

Show

Modbus
* Removed debug messages for RS485 Modbus TX and RX.

49.32

 Modbus RTU - Modbus RTU is available on RS2, RS4, AS1, AS2.

14 Feb 13 

Show

* Modbus RTU is available on RS2, RS4, AS1, AS2. More than one of these can be active at the same time if required.
* Setup example:
SETUP(RS2)
{
baud = 19200;
parity = N;
rxi = Y;
txi = Y;
proto = ModbusRTU;
}
* All low level handling of Modbus requests and responses are handled by the TFT firmware and the 'interface' to the itronSMART code is via system variables (xxx refers to the interface name: RS2, RS4, AS1 or AS2):
Name Type Use
MB_xxx_MODE U8 Modbus mode (0=master, 1=slave)
MB_xxx_REGS PTR Pointer to a U16 array to hold read / write registers
MB_xxx_COILS PTR Pointer to a U8 array to hold read / write coil bit values
MB_xxx_SLAVEID U8 Slave address
MB_xxx_FUNC U8 Modbus function code (1/3/15/16)
MB_xxx_REGOFF U16 Offset into U16 register array for read / write action
MB_xxx_REGCNT U16 Number of registers to read / write (or have been read / written)
MB_xxx_COILOFF U16 Offset into U16 coil register array for read / write action
MB_xxx_COILCNT U16 Number of coil registers to read / write (or have been read / written)
MB_xxx_STATUS U8 Modbus status (0=idle, 1=tx, 1=rx, 2+ error)
MB_xxx_TIMEOUT U16 Response timeout in milliseconds
MB_xxx_DATAOK PTR Pointer to function that is called after a successful transaction
MB_xxx_DATAERR PTR Pointer to function that is called after an error has occurred
* COIL data is converted from bits to bytes to make itronSMART access easier. For example a Modbus request to read 12 coils (2 Modbus data bytes) with offset of 6 will result in bytes 6 – 17 in the U8 coil registers being used.
* Master operation
Write regs / coils
Load values into U16 / U8 variable that MB_xxx_REGS/MB_xxx_COILS points to.
Setup MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_xxx_COILOFF and use command LOAD(RS2, 1); to start request.
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the success response is received from the slave.
Read regs / coils
Setup MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_xxx_COILOFF and use command LOAD(RS2, 1); to start request.
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the success response is received from the slave.
Values can then be read from variable that MB_xxx_REGS/MB_xxx_COILS points to.
* Slave operation
Write regs / coils
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the successful request from the master.
Values can then be read from variable that MB_xxx_REGS/MB_xxx_COILS points to.
MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_xxx_COILOFF values should be read to know which registers were updated.
Read regs / coils
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the successful request from the master.
MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_COILOFF values can be read to know which registers were read.
* Fixed interrupt handling

49.03

 MODBUS RTU Master - Added MODBUS protocol to handle CRC calculation and response events.

01 Jun 12 

Show

Added MODBUS protocol to handle CRC calculation and response events.
* The following parameters have been added to the serial port setups :-
> proto = Modbus;
> func = funcprefix;
> timeout = n;
* In Modbus mode the LOAD(port, .....) is handled differently, parameters function as follows :-
> LOAD(portname, address, function, register, variable);
- portname = RS2, RS4, etc
- address = Modbus slave address
- function (modbus function code) = 3 or 16 (3=read from slave, 16=write to slave)
- register = register location in slave to read / write
- variable = data to write (or destination variable for data that is read) (only U32 or FLT supported)
* For read actions a function will be called, the name of which depends on the func prefix and the address, function and register values.
* Example (assuming func prefix of 'mod' :-
> LOAD(RS4, 3, 3, 10, u23var); // read u32 value from register 10, slave address 3 and store in u32var
On success :-
FUNC(mod_3_3_10)
{
// do something with u32var...
}
On failure or timeout :-
FUNC(mod_3_3_10_ERR)
{
// an error has occurred trying to read...
}

49.00

 CRC-16 Support - Support for additional CRC-16 algorithms has been implemented.

22 Mar 12 

Show

Support for additional CRC-16 algorithms has been implemented.
* CALC( dst16, srcBuf, "type", "CRC16" );
> Note CALC( dst32, srcBuf, "", "CRC16" ); will use the MODBUS ("modbus") algorithm