MODBUS/TCP for QNX 6?

Are there options (freeware/shareware or commercial) for
MODBUS/TCP?

If so, what are the good ones!


Jeff Maass jmaass@columbus.rr.com Located near Columbus Ohio
USPSA # L-1192 NROI/CRO Amateur Radio K8ND
Maass’ IPSC Resources Page: http://home.columbus.rr.com/jmaass

It is application level protocol used on top of TCP/IP stack. It only
describes how you data is structured while being transmitted between client
and server. On my opinion it is easier to adopt MODBUS structure to a
particular application than write general purpose library. The protocol is
extendable. It has a number predefined operations as “read register”, “write
register”, and etc., but you can add something application specific. All you
need to know about the protocol is here: http://www.modbus.org. Just take a
look at specification and you’ll figure out how to deal with it.

Serge

“Jeff Maass” <jmaass@columbus.rr.com> wrote in message
news:b45lnv$t0$1@inn.qnx.com

Are there options (freeware/shareware or commercial) for
MODBUS/TCP?

If so, what are the good ones!


Jeff Maass > jmaass@columbus.rr.com > Located near Columbus Ohio
USPSA # L-1192 NROI/CRO Amateur Radio K8ND
Maass’ IPSC Resources Page: > http://home.columbus.rr.com/jmaass

Jeff Maass wrote:

Are there options (freeware/shareware or commercial) for
MODBUS/TCP?

If so, what are the good ones!


Jeff Maass > jmaass@columbus.rr.com > Located near Columbus Ohio
USPSA # L-1192 NROI/CRO Amateur Radio K8ND
Maass’ IPSC Resources Page: > http://home.columbus.rr.com/jmaass

\


Joerg Hering
Chief Developer VDR
e-mail: jhering@avecs-bergen.de
privat: hering.ruegen@t-online.de
mobile: jhering.ruegen@gmx.de

AVECS Bergen GmbH
development department
Billrothstraße 11a
D-18528 Bergen auf Rügen

Tel.: +49 3838 2119101
Fax: +49 3838 2119105
Mobile: +49 172 3896974
URL: http://www.avecs-bergen.de


Jorg:

Thanks for the reply.

Unfortunately, we need the slave-side code!

Does anyone have or know of a source (commercial, shareware,
open source) for the slave side of the Modbus over TCP/IP
protocol?


Jeff Maass jmaass@columbus.rr.com Located near Columbus Ohio
USPSA # L-1192 NROI/CRO Amateur Radio K8ND
Maass’ IPSC Resources Page: http://home.columbus.rr.com/jmaass


“Jörg Hering” <jhering@avecs-bergen.de> wrote in message
news:3E6711EB.2010201@avecs-bergen.de

Jeff Maass wrote:

Are there options (freeware/shareware or commercial) for
MODBUS/TCP?

If so, what are the good ones!


Jeff Maass > jmaass@columbus.rr.com > Located near Columbus Ohio
USPSA # L-1192 NROI/CRO Amateur Radio K8ND
Maass’ IPSC Resources Page: > http://home.columbus.rr.com/jmaass




\


Joerg Hering
Chief Developer VDR
e-mail: > jhering@avecs-bergen.de
privat: > hering.ruegen@t-online.de
mobile: > jhering.ruegen@gmx.de

AVECS Bergen GmbH
development department
Billrothstraße 11a
D-18528 Bergen auf Rügen

Tel.: +49 3838 2119101
Fax: +49 3838 2119105
Mobile: +49 172 3896974
URL: > http://www.avecs-bergen.de


\





/*

  • This is a master for the Modbus protocol over TCP/IP connections.
  • May, 1999.
  • Author: Luis Miguel Diego Alvarez

/
#include “mycurses.h”
#include <modbus.h
#include <sys/types.h
#include <sys/socket.h
#include <netinet/in.h
#include <netdb.h
#include <errno.h
#include <unistd.h
#include <fcntl.h
#include <string.h
/
select stuff /
#include <stdio.h
#include <sys/time.h
#include <unistd.h
/
signal handler stuff */
#include <signal.h

#define ERROR(s) {fprintf(stderr,"%d-",errno);perror(s);return(-1);}

static void display_menu (int,int);
static int initTest (WINDOW **);

//static void ReadSingleCoil (WINDOW *);
static void ReadMultipleCoils (WINDOW *);
//static void ReadSingleRegister (WINDOW *);
static void ReadMultipleRegisters (WINDOW *);

static void WriteSingleCoil (WINDOW *);
static void WriteMultipleCoils (WINDOW *);
static void WriteSingleRegister (WINDOW *);
static void WriteMultipleRegisters (WINDOW *);

static int SendModbusMessage(char, int, int);
static void RecvModbusMessage(char, int, int, int, WINDOW *);

static void NullFunction(WINDOW *);
static void ChangeSpeed(WINDOW *);
static void SetNumberOfProbes(WINDOW *win);

char speed_text[256];
struct commands
{
char *text;
void (*function)(WINDOW *);
};
typedef struct commands COMMAND;

COMMAND command[] =
{
//{“Read Single Coil”, ReadSingleCoil},
{“Read Multiple Coils”, ReadMultipleCoils },
//{“Read Single Register”, ReadSingleRegister},
{“Read Multiple Registers”, ReadMultipleRegisters},
{“Write Single Coil”, WriteSingleCoil},
{“Write Multiple Coils”, WriteMultipleCoils},
{“Write Single Register”, WriteSingleRegister},
{“Write Multiple Registers”, WriteMultipleRegisters},
{"------------------------", NullFunction},
{speed_text, ChangeSpeed},
{“Set number of probes”, SetNumberOfProbes}
};
#define MAX_OPTIONS ((sizeof(command)/sizeof(command[0])))

int width, height, number_of_packets;
int speed_fast;
int number_of_probes;
int modbus_socket;
int klemmenbusfall;
/*

  • Variables and functions for the getch “bug”
    */
    //int in_a_loop=0, out_of_the_loop=0;
    //static void break_loop(int sig);

int
main(int argc ,char *argv[])
{
WINDOW *win;
int key,old_option=(-1),new_option=0;
bool quit=FALSE;
struct sockaddr_in sa;
struct hostent *remote_host;

/*

  • Check parameters
    */

if(argc!=2) {
fprintf(stdout, “usage: %s hostname\n”, argv[0]);
exit(-1);
}

/*

  • Network stuff
    */

if((modbus_socket=socket(AF_INET, SOCK_STREAM, 0))<0)
ERROR(“socket”);

/*

  • Due to an identifier field error message when reading registers
  • so fast as I can, I suspect that the socket buffer is not fully
  • emptied. To avoid this problem, we declare the socket as non
  • blocking and read till recv returns -1
    */


    if((remote_host = gethostbyname(argv[1])) == (struct hostent *) NULL)
    ERROR(“gethostbyname”);

sa.sin_family = AF_INET;
(void)bcopy((char *)remote_host->h_addr, (char *)&sa.sin_addr,
remote_host->h_length);
sa.sin_port = htons(MODTCP_PORT);

if(connect(modbus_socket, (struct sockaddr )&sa, sizeof(sa))<0)
ERROR(“connect”);


//fcntl(modbus_socket, F_SETFL, O_NONBLOCK);
/

  • End of network stuff
    */

/*

  • Install handler for the Ctrl-C
    */

//(void) signal(SIGINT, break_loop);

memcpy(speed_text, “One request per second”,strlen(“One request per
second”));
speed_fast = 0;
number_of_probes = 10;
klemmenbusfall=0;

#ifdef PDCDEBUG
PDC_debug(“testcurs started\n”);
#endif
if (!initTest (&win)) exit(EXIT_FAILURE);

#ifdef A_COLOR
if (has_colors())
{
init_pair(1,COLOR_WHITE,COLOR_BLUE);
wattrset(win, COLOR_PAIR(1));
}
else
wattrset(win, A_REVERSE);
#else
wattrset(win, A_REVERSE);
#endif

erase();
display_menu(old_option,new_option);
while(1)
{
noecho();
keypad(stdscr,TRUE);
raw();
key = getch();
switch(key)
{
case 10:
case 13:
case KEY_ENTER:
erase();
refresh();
(*command[new_option].function)(win);
erase();
display_menu(old_option,new_option);
break;
case KEY_UP:
new_option = (new_option == 0) ? new_option :
new_option-1;
display_menu(old_option,new_option);
break;
case KEY_DOWN:
new_option = (new_option == MAX_OPTIONS-1) ?
new_option : new_option+1;
display_menu(old_option,new_option);
break;
case ‘Q’:
case ‘q’:
quit = TRUE;
break;
default: break;
}
if (quit == TRUE)
break;
}

delwin (win);

endwin();
close(modbus_socket);
exit(EXIT_SUCCESS);
}

static
void Continue (WINDOW *win)
{
wmove(win, 10, 1);
mvwaddstr(win, 10, 1, " Press any key to continue");
wrefresh(win);
raw();
wgetch(win);
}

static
int initTest (WINDOW *win)
{
#ifdef PDCDEBUG
PDC_debug(“initTest called\n”);
#endif
#ifdef NCURSES_VERSION
trace(TRACE_MAXIMUM);
#endif
initscr();
#ifdef PDCDEBUG
PDC_debug(“after initscr()\n”);
#endif
#ifdef A_COLOR
if (has_colors())
start_color();
#endif
width = COLS;
height = LINES; /
Create a drawing window */
*win = newwin(height, width, (LINES-height)/2, (COLS-width)/2);
if(win == NULL)
{ endwin();
return 0;
}
return 1;
}
/

static void
ReadSingleCoil (WINDOW *win)
{
int num;
int i=0;
int response_length;

wclear(win);
mvwaddstr(win, 6, 2, “Enter the address of the coil”);
echo();
noraw();
mvwscanw(win, 7, 6, “%d”, &num);
//mvwprintw(win, 8, 6, “Number: %d”, num);
nodelay(win, TRUE);
for(;:wink:
{
response_length=SendModbusMessage(READ_SINGLE_COIL, num, 0);
RecvModbusMessage(READ_SINGLE_COIL, num, 0, response_length, win);
if(wgetch(win)!=ERR) break;
}
nodelay(win, FALSE);
Continue(win);
}
*/

static void
ReadMultipleCoils (WINDOW *win)
{
int base_address, number;
//int key;
// int out_of_the_loop=0;
int response_length;

wclear(win);
mvwaddstr(win, 6, 2, “Address and the number of coils seperated by
space”);
wrefresh(win);
echo();
noraw();
mvwscanw(win, 7, 6, “%d %d”, &base_address, &number);
//mvwprintw(win, 8, 6, “Base address: %d Number: %d”, base_address,
number);
//nodelay(stdscr, TRUE);
number_of_packets = 0;
//in_a_loop =1;
for(;:wink:
{
number_of_packets++;
response_length=SendModbusMessage(READ_COILS, base_address, number);
RecvModbusMessage(READ_COILS, base_address, number, response_length, win);
if (speed_fast==0) sleep(1);
if(number_of_packets==number_of_probes) break;
// if(out_of_the_loop==1) break;
//key=getch();
//if(key!=ERR) break;
}
//nodelay(stdscr, FALSE);
//out_of_the_loop=0;
Continue(win);
}
/*
static void
ReadSingleRegister (WINDOW *win)
{
int num, response_length;

wclear(win);
mvwaddstr(win, 6, 2, “Address of the register”);
echo();
noraw();
mvwscanw(win, 7, 6, “%d”, &num);
//mvwprintw(win, 8, 6, “Base Address: %d”, num);
nodelay(win, TRUE);
for(;:wink:
{
response_length=SendModbusMessage(READ_SINGLE_REGISTER, num, 0);
RecvModbusMessage(READ_SINGLE_REGISTER, num, 0, response_length, win);
if(wgetch(win)!=ERR) break;
}
nodelay(win, FALSE);
Continue(win);
}
*/
static void
ReadMultipleRegisters (WINDOW *win)
{
int base_address;
int number, response_length;

wclear(win);
mvwaddstr(win, 6, 2, “Address and number of registers seperated by
space”);
wrefresh(win);
echo();
noraw();
mvwscanw(win, 7, 6, “%d %d”, &base_address,&number);
//mvwprintw(win, 8, 6, “Base Address: %s Number: %d”, base_address,
number);
//nodelay(win, TRUE);
number_of_packets = 0;
for(;:wink:
{
//in_a_loop = 1;
number_of_packets++;
response_length = SendModbusMessage(READ_MULTIPLE_REGISTERS, base_address,
number);
RecvModbusMessage(READ_MULTIPLE_REGISTERS, base_address, number,
response_length, win);
if (speed_fast==0) sleep(1);
if(number_of_packets==number_of_probes) break;
//if(out_of_the_loop) break;
//if(getch()!=ERR) break;
}
//nodelay(win, FALSE);
//in_a_loop = 0;
//out_of_the_loop = 0;
Continue(win);
}

static void
WriteSingleCoil (WINDOW *win)
{
int base_address;
int number, response_length;
for(;:wink:
{
wclear(win);
mvwaddstr(win, 6, 2, “Enter address and value (0/1) for the coil”);
wrefresh(win);
echo();
noraw();
mvwscanw(win, 7, 6, “%d %d”, &base_address,&number);
//mvwprintw(win, 8, 6, “Base Address: %s Number: %d”, base_address,
number);
if((number!=0)&&(number!=1))
{
wclear(win);
mvwaddstr(win, 6, 2, “The value must be 0 or 1 !!!”);
wrefresh(win);
//nodelay(win, TRUE);
wgetch(win);
//nodelay(win, FALSE);
} else break;
}
response_length=SendModbusMessage(WRITE_COIL, base_address, number);
RecvModbusMessage(WRITE_COIL, base_address, number, response_length, win);
Continue(win);
}

static void
WriteMultipleCoils (WINDOW *win)
{
int base_address;
int number_of_coils;
int number, response_length;
int i,j;
unsigned char *pointer;
unsigned char mask=0;

pointer = (unsigned char *)ModbusBuffer + WMC_OFFSET;

wclear(win);
mvwaddstr(win, 6, 2, “Enter base address and number of coils”);
wrefresh(win);
echo();
noraw();
mvwscanw(win, 7, 6, “%d %d”, &base_address,&number_of_coils);

for(i=1;i<=number_of_coils;i++)
{
for(;:wink:
{
wclear(win);
mvwprintw(win, 6 ,2 , “Enter value for coil %d”, i);
wrefresh(win);
echo();
noraw();
mvwscanw(win, 7, 6, “%d”, &number);

if((number!=0)&&(number!=1))
{
wclear(win);
mvwaddstr(win, 6, 2, “The value must be 0 or 1 !!!”);
wrefresh(win);
//nodelay(win, TRUE);
wgetch(win);
//nodelay(win, FALSE);
} else break;
} //for(;:wink:
j=i%8;
if(j==0)
{
mask|=(number<<7);
*pointer=mask;
pointer++;
mask=0;
} else mask|=(number<<(j-1));

} //for(number_of_coils)
*pointer=mask;

response_length=SendModbusMessage(FORCE_MULTIPLE_COILS, base_address,
number_of_coils);
RecvModbusMessage(FORCE_MULTIPLE_COILS, base_address, number_of_coils,
response_length, win);
Continue(win);
}

static void
WriteSingleRegister(WINDOW *win)
{

int base_address;
int number, response_length;

wclear(win);
mvwaddstr(win, 6, 2, “Enter address and value for the register”);
wrefresh(win);
echo();
noraw();
mvwscanw(win, 7, 6, “%d %d”, &base_address, &number);
//mvwprintw(win, 8, 6, “Base address: %d Number: %d”, base_address,
number);

response_length=SendModbusMessage(WRITE_SINGLE_REGISTER, base_address,
number);
RecvModbusMessage(WRITE_SINGLE_REGISTER, base_address, number,
response_length, win);

Continue(win);
}

static void
WriteMultipleRegisters(WINDOW *win)
{

int base_address;
int number_of_registers;
int number, response_length;
int i;
unsigned short *pointer;

pointer = (unsigned short *)((unsigned char *)ModbusBuffer + WMR_OFFSET);

wclear(win);
mvwaddstr(win, 6, 2, “Enter base address and number of registers”);
wrefresh(win);
echo();
noraw();
mvwscanw(win, 7, 6, “%d %d”, &base_address,&number_of_registers);

for(i=1;i<=number_of_registers;i++)
{
wclear(win);
mvwprintw(win, 6 ,2 , “Enter value for register %d”, i);
wrefresh(win);
echo();
noraw();
mvwscanw(win, 7, 6, “%d”, &number);
*pointer = htons(number);
pointer++;
}

response_length=SendModbusMessage(WRITE_MULTIPLE_REGISTERS, base_address,
number_of_registers);
RecvModbusMessage(WRITE_MULTIPLE_REGISTERS, base_address,
number_of_registers, response_length, win);
Continue(win);
}

static void
display_menu(int old_option,int new_option)
{
register size_t i;

attrset(A_NORMAL);
mvaddstr(3,20,“TCP/Modbus Master Program”);

for (i=0;i<MAX_OPTIONS;i++)
mvaddstr(5+i,25,command> _.text);
if (old_option != (-1))
mvaddstr(5+old_option,25,command[old_option].text);
attrset(A_REVERSE);
mvaddstr(5+new_option,25,command[new_option].text);
attrset(A_NORMAL);
mvaddstr(18,3,“Use Up and Down Arrows to select - Enter to run - Q to
quit”);
refresh();
}

static int
SendModbusMessage(char type, int base_address, int number)
{

union Modtcp_request * data_pointer;
struct Modtcp_header * header_pointer;
int len;
int response_length;

header_pointer = (struct Modtcp_header )ModbusBuffer;
data_pointer = (union Modtcp_request )((char )ModbusBuffer+8);

switch(type)
{
case READ_COILS:
/
Fill header /
header_pointer->id = htons(MODTCP_ID);
header_pointer->protocol = 0;
header_pointer->length = htons(READ_COILS_DATA_LENGTH);
header_pointer->unit = 1;
header_pointer->fc = READ_COILS;
/
Fill data /
data_pointer->rc.ReferenceNumber = htons(base_address);
data_pointer->rc.BitCount = htons(number);
len = READ_COILS_TOTAL_LENGTH;
response_length = HEADER_LENGTH + RMC_FIXED_LENGTH + RMC_VAR_LENGTH;
break;
case READ_MULTIPLE_REGISTERS:
/
Fill header /
header_pointer->id = htons(MODTCP_ID);
header_pointer->protocol = 0;
header_pointer->length = htons(READ_MULTIPLE_REGISTERS_DATA_LENGTH);
header_pointer->unit = 1;
header_pointer->fc = READ_MULTIPLE_REGISTERS;
/
Fill data /
data_pointer->rmr.ReferenceNumber = htons(base_address);
data_pointer->rmr.WordCount = htons(number);
len = READ_MULTIPLE_REGISTERS_TOTAL_LENGTH;
response_length = HEADER_LENGTH+RMR_FIXED_LENGTH+RMR_VAR_LENGTH;
break;
case WRITE_COIL:
/
Fill header /
header_pointer->id = htons(MODTCP_ID);
header_pointer->protocol = 0;
header_pointer->length = htons(WRITE_COIL_DATA_LENGTH);
header_pointer->unit = 1;
header_pointer->fc = WRITE_COIL;
/
Fill data /
data_pointer->wc.ReferenceNumber = htons(base_address);
if(number==1) data_pointer->wc.On_or_Off = 0xFF;
else data_pointer->wc.On_or_Off = 0x00;
data_pointer->wc.MustBeNull = 0x00;
len = WRITE_COIL_TOTAL_LENGTH;
response_length = HEADER_LENGTH+WC_FIXED_LENGTH+WC_VAR_LENGTH;
break;
case FORCE_MULTIPLE_COILS:
/
Fill header /
header_pointer->id = htons(MODTCP_ID);
header_pointer->protocol = 0;
header_pointer->length = htons(7+(number+7)/8);
header_pointer->unit = 1;
header_pointer->fc = FORCE_MULTIPLE_COILS;
/
Fill data /
data_pointer->fmc.ReferenceNumber = htons(base_address);
data_pointer->fmc.BitCount = htons(number);
data_pointer->fmc.ByteCount = (number+7)/8;
len = 7+6+(number+7)/8;
response_length = HEADER_LENGTH+FMC_FIXED_LENGTH+FMC_VAR_LENGTH;
break;
case WRITE_SINGLE_REGISTER:
/
Fill header /
header_pointer->id = htons(MODTCP_ID);
header_pointer->protocol = 0;
header_pointer->length = htons(WRITE_SINGLE_REGISTER_DATA_LENGTH);
header_pointer->unit = 1;
header_pointer->fc = WRITE_SINGLE_REGISTER;
/
Fill data /
data_pointer->wsr.ReferenceNumber = htons(base_address);
#ifdef MOTOROLA
data_pointer->wsr.RegisterValue = number;
#else
data_pointer->wsr.RegisterValue = htons(number);
#endif //MOTOROLA
len = WRITE_SINGLE_REGISTER_TOTAL_LENGTH;
response_length = HEADER_LENGTH+WSR_FIXED_LENGTH+WSR_VAR_LENGTH;
break;
case WRITE_MULTIPLE_REGISTERS:
/
Fill header /
header_pointer->id = htons(MODTCP_ID);
header_pointer->protocol = 0;
header_pointer->length = htons(7+(number
2));
header_pointer->unit = 1;
header_pointer->fc = WRITE_MULTIPLE_REGISTERS;
/
Fill data /
data_pointer->wmr.ReferenceNumber = htons(base_address);
data_pointer->wmr.WordCount = htons(number);
data_pointer->wmr.ByteCount = number
2;
len = 7+6+(number
2);
response_length = HEADER_LENGTH+WMR_FIXED_LENGTH+WMR_VAR_LENGTH;
break;
default:
break;
}

if(response_length>MODTCP_MAXLENGTH)
{
return(response_length);
}
send(modbus_socket, ModbusBuffer, len, 0);

return(response_length);
}



static void
RecvModbusMessage(char type, int base_address, int number, int
response_len, WINDOW *win)
{

union Modtcp_response * data_pointer;
struct Modtcp_header * header_pointer;
char * aux_pointer;
short * int_pointer;
//fd_set rfds;
//struct timeval tv;
//int retval;
int len, remain_len=response_len, recv_len=0, i;

if(remain_len>MODTCP_MAXLENGTH) WRITE_MESSAGE_TO_SCREEN(“You
request too many bytes !!”)

header_pointer = (struct Modtcp_header *)ModbusBuffer;
data_pointer = (union Modtcp_response *)((char )ModbusBuffer+8);



//for(;:wink:
// {
/
Select on the socket /
//FD_ZERO(&rfds);
//FD_SET(modbus_socket, &rfds);
///
Wait up to one second /
//tv.tv_sec = 1;
//tv.tv_usec = 0;
//retval = select(modbus_socket+1, &rfds, NULL, NULL, &tv);
// retval = select(modbus_socket+1, &rfds, NULL, NULL, NULL);

//if(retval==-1) ERROR(“select”);

// if (retval)
// {
/

  • Due to an identifier field error message when reading registers
  • so fast as I can, I suspect that the socket buffer is not fully
  • emptied. To avoid this problem, we declare the socket as non
  • blocking and read till recv returns -1
    /
    // while((len=recv(modbus_socket, ModbusBuffer+total_len,
    MODTCP_MAXLENGTH, 0))!=-1)
    //{
    //total_len+=len;
    //}
    mvwprintw(win, i+1, 0, “Waiting on recv”);
    wrefresh(win);

    while(remain_len)
    {
    if((len=recv(modbus_socket, ModbusBuffer+recv_len, remain_len,
    0))==-1) return;
    remain_len-=len;
    recv_len+=len;
    /
  • Check if it’s an exception from the server
    /
    if((header_pointer->fc)&0x80)
    {
    switch(header_pointer->data)
    {
    case ILLEGAL_FUNCTION:
    WRITE_MESSAGE_TO_SCREEN(“Illegal function exception!!”)
    case ILLEGAL_DATA_ADDRESS:
    WRITE_MESSAGE_TO_SCREEN(“Illegal data address exception!!”)
    case ILLEGAL_DATA_VALUE:
    WRITE_MESSAGE_TO_SCREEN(“Illegal data value exception!!”)
    case SLAVE_DEVICE_FAILURE:
    WRITE_MESSAGE_TO_SCREEN(“Slave device failure exception!!”)
    default:
    WRITE_MESSAGE_TO_SCREEN(“Unrecognized exception!!”)
    }
    }
    }

    /
  • Test the response from the server
    /

    if(ntohs(header_pointer->id)!=MODTCP_ID) {
    WRITE_MESSAGE_TO_SCREEN(“Identifier field is wrong!!”)
    }
    if(ntohs(header_pointer->protocol)!=0) WRITE_MESSAGE_TO_SCREEN(“Protocol
    field is wrong!!”)

    //if(header_pointer->fc!=type) WRITE_MESSAGE_TO_SCREEN(“Function code is
    wrong!!”);

    /
  • Print message to screen
    */

    switch(type)
    {
    case READ_COILS:
    aux_pointer=&(data_pointer->rc.BitValues[0]);
    wclear(win);
    wmove(win, 0, 0);
    mvwprintw(win, 1, 0, “Number of bytes received: %d Number of packets
    received: %d”, data_pointer->rc.ByteCount, number_of_packets);
    for(i=1;(i<20)&&(i<=data_pointer->rc.ByteCount);i++)
    {
    mvwprintw(win, i+1, 0, “Bits %d to %d: %d %d %d %d %d %d %d %d”, (i-1)*8,
    (i-1)*8+7,(((*aux_pointer)&0x01)==0) ? 0:1, (((*aux_pointer)&0x02)==0) ?_

0:1, (((*aux_pointer)&0x04)==0) ? 0:1, (((*aux_pointer)&0x08)==0) ? 0:1,
(((*aux_pointer)&0x10)==0) ? 0:1, (((*aux_pointer)&0x20)==0) ? 0:1,
(((*aux_pointer)&0x40)==0) ? 0:1, (((*aux_pointer)&0x80)==0) ? 0:1);

aux_pointer++;
}
mvwprintw(win, i+1, 0, “Total number of probes: %d”, number_of_probes);
wrefresh(win);
break;
case READ_MULTIPLE_REGISTERS:
int_pointer=(short *)&(data_pointer->rmr.RegisterValues[0]);
wclear(win);
wmove(win, 0, 0);
mvwprintw(win, 1, 0, “Number of bytes received: %d Number of packets
received: %d”, data_pointer->rmr.ByteCount, number_of_packets);
for(i=1;(i<10)&&(i<=(data_pointer->rmr.ByteCount)/2); i++)
{
mvwprintw(win, i+1, 0, "Register %d: %d ", i, ntohs(*int_pointer));
int_pointer++;
}
mvwprintw(win, i+2, 0, “Total number of probes: %d”, number_of_probes);
#ifdef KBUS_TIME
i++;
int_pointer=(short
)&(data_pointer->rmr.RegisterValues[data_pointer->rmr.ByteCount]);
mvwprintw(win, i+2, 0, “Klemmenbus time for the last cycle: %f ms”,
(
((unsigned short *)int_pointer))0.0032);
//if((
((unsigned short )aux_pointer))>31250) klemmenbusfall++;
int_pointer++;
i++;
mvwprintw(win, i+2, 0, “Maximum Klemmenbus time : %f ms”,(
((unsigned
short *)int_pointer))0.0032);
int_pointer++;
i++;
mvwprintw(win, i+2, 0, “Maximum main time : %f ms”, (
((unsigned short *)
int_pointer))0.0032);
int_pointer++;
i++;
mvwprintw(win, i+2, 0, “Number of Klemmenbus errors: %d”, (
((unsigned
short *) int_pointer)));
i++;
mvwprintw(win, i+2, 0, “Bytes returned by receive: %d”, recv_len);
#endif //KBUS_TIME
wrefresh(win);
break;
case WRITE_COIL:
wclear(win);
mvwprintw(win, 6, 2, “Coil number %d set to %d”,
ntohs(data_pointer->wc.ReferenceNumber),(data_pointer->wc.On_or_Off==0) ?

0:1);

wrefresh(win);
wgetch(win);
break;
case FORCE_MULTIPLE_COILS:
wclear(win);
mvwprintw(win, 6, 2, “%d coils from address %d were set”,
ntohs(data_pointer->fmc.BitCount),

ntohs(data_pointer->fmc.ReferenceNumber));

wrefresh(win);
wgetch(win);
break;
case WRITE_SINGLE_REGISTER:
wclear(win);
#ifdef MOTOROLA
mvwprintw(win, 6, 2, “The value %d was written in the register %d”,
data_pointer->wsr.RegisterValue, ntohs(data_pointer->wsr.ReferenceNumber));
#else
mvwprintw(win, 6, 2, “The value %d was written in the register %d”,
ntohs(data_pointer->wsr.RegisterValue),

ntohs(data_pointer->wsr.ReferenceNumber));

#endif //MOTOROLA
wrefresh(win);
wgetch(win);
break;
case WRITE_MULTIPLE_REGISTERS:
wclear(win);
mvwprintw(win, 6, 2, “%d registers from address %d were written”,
ntohs(data_pointer->wmr.WordCount),

ntohs(data_pointer->wmr.ReferenceNumber));

wrefresh(win);
wgetch(win);
break;
default:
break;
}
return;
// } //if select on socket
// else
// {
// WRITE_MESSAGE_AND_DONT_RETURN(“No response received !!!”);
// }
// } //for(;:wink:

return;

}

static void NullFunction(WINDOW *win)
{
return;
}

static void ChangeSpeed(WINDOW *win)
{
if(speed_fast)
{
speed_fast=0;
memcpy(speed_text, “One request per second”,strlen(“One request per
second”));
}
else
{
speed_fast=1;
memcpy(speed_text, “So fast as I can do it”,strlen(“So fast as I can do
it”));
}
}

static void SetNumberOfProbes(WINDOW win)
{

wclear(win);
mvwaddstr(win, 6, 2, “Enter the number of requests”);
wrefresh(win);
echo();
noraw();
mvwscanw(win, 7, 6, “%d”, &number_of_probes);
Continue(win);
}

/

static void
break_loop(int sig)
{
if(in_a_loop)
{
out_of_the_loop=1;
in_a_loop=0;
}
return;
}
*/

----------------------------------------------------------------------------
----



_/*

  • tcpmod.h
  • Header file for the Modbus/TCP Protocol

/

/

  • Modbus master specific declarations
    /

    unsigned char ModbusBuffer[500];

    #define WMC_OFFSET 13
    #define WMR_OFFSET 13

    #define MODTCP_ID 0x1234
    #define NUMBER_OF_SCANS 10

    /
  • Declarations and defines from the implementation of the Modbus/TCP
  • protocol for the Siemens C165 microcontroller
    /

    /
  • Maximum length of a MODTCP message:
  • -Read Input Registers: Read 125 words
  • -Size=250+Byte Count+Function Code+ Unit ID+ 6 Bytes MODTCP Header
    /

    #define MODTCP_MAXLENGTH 259
    #define MODTCP_PORT 502

    /
  • Function codes
    /

    /
  • Class 0
    /
    #define READ_MULTIPLE_REGISTERS 0x03
    #define WRITE_MULTIPLE_REGISTERS 0x10
    /
  • Class 1
    /
    #define READ_COILS 0x01
    #define READ_INPUT_DISCRETES 0x02
    #define READ_INPUT_REGISTERS 0x04
    #define WRITE_COIL 0x05
    #define WRITE_SINGLE_REGISTER 0x06
    #define READ_EXCEPTION_STATUS 0x07
    /
  • Class 2
    /
    #define FORCE_MULTIPLE_COILS 0x0F
    /
  • Class ?
    /
    #define FETCH_COMM_EVENT_COUNTER 0x0B

    /
  • Exception codes
    /
    #define ILLEGAL_FUNCTION 0x01
    #define ILLEGAL_DATA_ADDRESS 0x02
    #define ILLEGAL_DATA_VALUE 0x03
    #define SLAVE_DEVICE_FAILURE 0x04

    /
  • Fixed lengths for some messages
    /

    #define READ_COILS_DATA_LENGTH 0x06
    #define READ_COILS_TOTAL_LENGTH 0x0C
    #define READ_MULTIPLE_REGISTERS_DATA_LENGTH 0x06
    #define READ_MULTIPLE_REGISTERS_TOTAL_LENGTH 0x0C
    #define WRITE_COIL_DATA_LENGTH 0x06
    #define WRITE_COIL_TOTAL_LENGTH 0x0C
    #define WRITE_SINGLE_REGISTER_DATA_LENGTH 0x06
    #define WRITE_SINGLE_REGISTER_TOTAL_LENGTH 0x0C

    /
  • Structure for the first bytes of a message
    /

    struct Modtcp_header {
    unsigned short id;
    short protocol;
    unsigned short length;
    char unit;
    char fc; /
    Function Code*/
    char data; /First byte of data/
    };
    /*
  • defines for the expected length of the response
    /

    #define HEADER_LENGTH 8

    #define RMC_FIXED_LENGTH 1
    #define RMC_VAR_LENGTH (number+7)/8

    #ifdef KBUS_TIME
    #define RMR_FIXED_LENGTH 9
    #define RMR_VAR_LENGTH number
    2
    #else
    #define RMR_FIXED_LENGTH 1
    #define RMR_VAR_LENGTH number2
    #endif

    #define WC_FIXED_LENGTH 4
    #define WC_VAR_LENGTH 0

    #define FMC_FIXED_LENGTH 4
    #define FMC_VAR_LENGTH 0

    #define WSR_FIXED_LENGTH 4
    #define WSR_VAR_LENGTH 0

    #define WMR_FIXED_LENGTH 4
    #define WMR_VAR_LENGTH 0




    /
  • These structures map the Modtcp requests and responses. Although in
  • some cases this information may be redundant, it lets the programmer
  • easily add support for new function codes.
    /

    /
  • Read multiple registers
    /

    struct Read_multiple_registers_request {
    short ReferenceNumber;
    short WordCount;
    };

    #define READ_MULT_REGS_MAX 125

    struct Read_multiple_registers_response {
    unsigned char ByteCount;
    char RegisterValues[READ_MULT_REGS_MAX
    2];
    };

    /*
  • Write multiple registers
    /

    #define WRITE_MULT_REGS_MAX 100

    struct Write_multiple_registers_request {
    short ReferenceNumber;
    short WordCount;
    char ByteCount;
    char RegisterValues[WRITE_MULT_REGS_MAX
    2];
    };

    struct Write_multiple_registers_response {
    short ReferenceNumber;
    short WordCount;
    };

    /*
  • Read coils
    /

    #define READ_COILS_MAX 2000

    struct Read_coils_request {
    short ReferenceNumber;
    short BitCount;
    };

    struct Read_coils_response {
    unsigned char ByteCount;
    char BitValues[READ_COILS_MAX/8];
    };

    /
  • Read inputs discretes
    /

    #define READ_INPUTS_DISCRETES_MAX 2000

    struct Read_inputs_discretes_request {
    short ReferenceNumber;
    short BitCount;
    };

    struct Read_inputs_discretes_response {
    unsigned char ByteCount;
    char BitValues[READ_INPUTS_DISCRETES_MAX/8];
    };

    /
  • Read input registers
    /

    #define READ_INPUT_REGISTERS_MAX 125

    struct Read_input_registers_request {
    short ReferenceNumber;
    short WordCount;
    };

    struct Read_input_registers_response {
    unsigned char ByteCount;
    char RegisterValues[READ_INPUT_REGISTERS_MAX
    2];
    };

    /*
  • Write coil
    /

    struct Write_coil_request {
    short ReferenceNumber;
    char On_or_Off;
    char MustBeNull;
    };

    struct Write_coil_response {
    short ReferenceNumber;
    char On_or_Off;
    char MustBeNull;
    };

    /
  • Write single register
    /

    struct Write_single_register_request {
    short ReferenceNumber;
    short RegisterValue;
    };

    struct Write_single_register_response {
    short ReferenceNumber;
    short RegisterValue;
    };

    /
  • Force multiple coils
    /

    #define FORCE_MULTIPLE_COILS_MAX 800

    struct Force_multiple_coils_request {
    short ReferenceNumber;
    short BitCount;
    unsigned char ByteCount;
    char Data[FORCE_MULTIPLE_COILS/8];
    };

    struct Force_multiple_coils_response {
    short ReferenceNumber;
    short BitCount;
    };

    /
  • Fetch Comm Event Counter: no structure for the request
    /

    struct Fetch_comm_event_counter_response {
    short Status;
    short EventCount;
    };

    /
  • Union of Modbus/TCP requests
    /

    union Modtcp_request {
    struct Read_multiple_registers_request rmr;
    struct Write_multiple_registers_request wmr;
    struct Read_coils_request rc;
    struct Read_inputs_discretes_request rid;
    struct Read_input_registers_request rir;
    struct Write_coil_request wc;
    struct Write_single_register_request wsr;
    struct Force_multiple_coils_request fmc;
    };

    /
  • Union of Modbus/TCP responses
    */

    union Modtcp_response {
    struct Read_multiple_registers_response rmr;
    struct Write_multiple_registers_response wmr;
    struct Read_coils_response rc;
    struct Read_inputs_discretes_response rid;
    struct Read_input_registers_response rir;
    struct Write_coil_response wc;
    struct Write_single_register_response wsr;
    struct Force_multiple_coils_response fmc;
    struct Fetch_comm_event_counter_response fcec;
    };



    #define WRITE_MESSAGE_TO_SCREEN(message) {wclear(win); mvwaddstr(win, 6,
    2, message);wrefresh(win);return;}
    #define WRITE_MESSAGE_AND_DONT_RETURN(message) {wclear(win);
    mvwaddstr(win, 6, 2, message);wrefresh(win);}_

----------------------------------------------------------------------------
----



/***************************************************************************

***

_* Copyright 1996 by Thomas E. Dickey <> dickey@clark.net
*

  • All Rights Reserved.
  • Permission to use, copy, modify, and distribute this software and its
  • documentation for any purpose and without fee is hereby granted,
    provided *
  • that the above copyright notice appear in all copies and that both that
  • copyright notice and this permission notice appear in supporting
  • documentation, and that the name of the above listed copyright
    holder(s) *
  • not be used in advertising or publicity pertaining to distribution of
    the *
  • software without specific, written prior permission. THE ABOVE LISTED
  • COPYRIGHT HOLDER(S) DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
    SOFTWARE, *
  • INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  • EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
  • SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
  • RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
  • CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  • CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    *

    ****************************************************************************_

**/

_/* $Id: test.priv.h,v 1.6 1996/07/07 18:12:00 tom Exp $ /
#if HAVE_CONFIG_H
#include <config.h
#endif

#include <stdlib.h
#include <sys/types.h

#if HAVE_UNISTD_H
#include <unistd.h
#endif

#include <curses.h

#if HAVE_GETOPT_H
#include <getopt.h
#else
/
‘getopt()’ may be prototyped in <stdlib.h>, but declaring its variables

  • doesn’t hurt.
    */
    extern char optarg;
    extern int optind;
    #endif /
    HAVE_GETOPT_H /

    #ifndef GCC_NORETURN
    #define GCC_NORETURN /
    nothing /
    #endif
    #ifndef GCC_UNUSED
    #define GCC_UNUSED /
    nothing */
    #endif

    #ifndef EXIT_SUCCESS
    #define EXIT_SUCCESS 0
    #endif
    #ifndef EXIT_FAILURE
    #define EXIT_FAILURE 1
    #endif_

Just to follow-up what I found and what we have done.

We found one source of a commercial Modbus/TCP slave-side
library for both QNX 6 and QNX 4, FOCUS Software Engineering
in Australia. See their web site at http://www.focus-sw.com/.

They have Master and Slave side products, as well as for
serial Modbus. The Slave-side protocol package is not listed on
their web site (yet), but if you inquire, it’s available.

Price is USD $250. Documentation (which they sent on request
for evaluation) looks very good, and we are awaiting delivery of
the software (by email). It’s delivered as source code, and there
is no royalty required for shipped products utilizing it (license is
in the documentation pack sent for review).

I have no connection with FOCUS, but thought I’d spread the word.


Jeff Maass jmaass@columbus.rr.com Located near Columbus Ohio
USPSA # L-1192 NROI/CRO Amateur Radio K8ND
Maass’ IPSC Resources Page: http://home.columbus.rr.com/jmaass



“Jeff Maass” <jmaass@columbus.rr.com> wrote in message
news:b45lnv$t0$1@inn.qnx.com

Are there options (freeware/shareware or commercial) for
MODBUS/TCP?

If so, what are the good ones!


Jeff Maass > jmaass@columbus.rr.com > Located near Columbus Ohio
USPSA # L-1192 NROI/CRO Amateur Radio K8ND
Maass’ IPSC Resources Page: > http://home.columbus.rr.com/jmaass