Thanks for the reply.
/*
- 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;
/*
if(argc!=2) {
fprintf(stdout, “usage: %s hostname\n”, argv[0]);
exit(-1);
}
/*
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);
/
/*
- 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(;
{
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(;
{
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(;
{
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(;
{
//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(;
{
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(;
{
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(;
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+(number2));
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 = number2;
len = 7+6+(number2);
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(;
// {
/ 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) ?_