/* modbus_rtu.c
By P.Costigan email: phil@pcscada.com.au http://pcscada.com.au
These library of functions are designed to enable a program send and
receive data from a device that communicates using the Modbus protocol.
Copyright (C) 2000 Philip Costigan P.C. SCADA LINK PTY. LTD.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The functions included here have been derived from the
Modicon Modbus Protocol Reference Guide
which can be obtained from Schneider at www.schneiderautomation.com.
This code has its origins with
paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk)
who wrote a small program to read 100 registers from a modbus slave.
I have used his code as a catalist to produce this more functional set
of functions. Thanks paul.
*/
#include <fcntl.h> /* File control definitions */
#include <stdio.h> /* Standard input/output */
#include <string.h>
#include <stdlib.h>
#include <termio.h> /* POSIX terminal control definitions */
#include <sys/time.h> /* Time structures for select() */
#include <unistd.h> /* POSIX Symbolic Constants */
#include <errno.h> /* Error definitions */
#include "modbus_rtu.h"
//#define DEBUG /* uncomment to see the data sent and received */
int char_interval_timeout;
/*************************************************************************
modbus_query( packet, length)
Function to add a checksum to the end of a packet.
Please note that the packet array must be at least 2 fields longer than
string_length.
**************************************************************************/
void modbus_query( unsigned char *packet , size_t string_length )
{
int temp_crc;
/* declaration */
unsigned int crc(unsigned char buf[],int start,int cnt);
temp_crc = crc( packet, 0, string_length );
packet[ string_length++ ] = temp_crc >> 8;
packet[ string_length++ ] = temp_crc & 0x00FF;
packet[ string_length ] = 0;
}
/***********************************************************************
send_query( file_descriptor, query_string, query_length )
Function to send a query out to a modbus slave.
************************************************************************/
int send_query( int ttyfd, unsigned char *query, size_t string_length )
{
int write_stat;
#ifdef DEBUG
int i;
#endif
modbus_query( query, string_length );
string_length += 2;
#ifdef DEBUG
// Print to stderr the hex value of each character that is about to be
// sent to the modbus slave.
for( i = 0; i < string_length; i++ )
{
fprintf( stderr, "[%0.2X]", query[ i ] );
}
fprintf( stderr, "\n" );
#endif
tcflush( ttyfd, TCIOFLUSH ); /* flush the input & output streams */
write_stat = write( ttyfd, query, string_length );
tcflush( ttyfd, TCIFLUSH ); /* maybe not neccesary */
return( write_stat );
}
/*********************************************************************
modbus_response( response_data_array, query_array )
Function to the correct response is returned and that the checksum
is correct.
Returns: string_length if OK
0 if failed
Less than 0 for exception errors
Note: All functions used for sending or receiving data via
modbus return these return values.
**********************************************************************/
int modbus_response( unsigned char *data, unsigned char *query, int fd )
{
int response_length;
unsigned short crc_calc = 0;
unsigned short crc_received = 0;
unsigned char recv_crc_hi;
unsigned char recv_crc_lo;
/* local declaration */
int receive_response( unsigned char *received_string, int ttyfd );
unsigned int crc(unsigned char buf[],int start,int cnt);
response_length = receive_response( data, fd );
if( response_length )
{
crc_calc = crc( data, 0, response_length - 2 );
recv_crc_hi = (unsigned) data[ response_length - 2 ];
recv_crc_lo = (unsigned) data[ response_length - 1 ];
crc_received = data[ response_length - 2 ];
crc_received = (unsigned) crc_received << 8;
crc_received =
crc_received |(unsigned)data[response_length -1];
/*********** check CRC of response ************/
if (crc_calc != crc_received)
{
fprintf( stderr, "crc error received ");
fprintf( stderr, "%0X - ", crc_received);
fprintf( stderr, "crc_calc %0X\n",crc_calc);
response_length = 0;
}
/********** check for exception response *****/
if( response_length && data[ 1 ] != query [ 1 ] )
{
response_length = 0 - data[ 2 ];
}
}
return( response_length );
}
/***********************************************************************
receive_response( array_for_data )
Function to monitor for the reply from the modbus slave.
This function blocks for timeout seconds if there is no reply.
Returns: Total number of characters received.
***********************************************************************/
int receive_response( unsigned char *received_string, int ttyfd )
{
int rxchar = PORT_FAILURE;
int data_avail = FALSE;
int bytes_received = 0;
int read_stat;
int timeout = 1; /* 1 second */
fd_set rfds;
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
FD_ZERO( &rfds );
FD_SET( ttyfd, &rfds );
#ifdef DEBUG
fprintf( stderr, "Waiting for response.\n");
#endif
/* wait for a response */
data_avail = select( FD_SETSIZE, &rfds, NULL, NULL, &tv );
if( !data_avail )
{
bytes_received = 0;
fprintf( stderr, "Comms time out\n" );
}
tv.tv_sec = 0;
tv.tv_usec = char_interval_timeout;
FD_ZERO( &rfds );
FD_SET( ttyfd, &rfds );
while( data_avail )
{
/* if no character at the buffer wait char_interval_timeout */
/* before accepting end of response */
if( select( FD_SETSIZE, &rfds, NULL, NULL, &tv ) )
{
read_stat = read( ttyfd, &rxchar, 1 );
if( read_stat < 0 )
{
bytes_received = PORT_FAILURE;
data_avail = FALSE;
}
else
{
rxchar = rxchar & 0xFF;
received_string[ bytes_received ++ ] = rxchar;
}
if( bytes_received >= MAX_RESPONSE_LENGTH )
{
bytes_received = PORT_FAILURE;
data_avail = FALSE;
}
#ifdef DEBUG
/* display the hex code of each character received */
fprintf( stderr, "<%0.2X>", rxchar );
#endif
}
else
{
data_avail = FALSE;
}
}
#ifdef DEBUG
fprintf( stderr, "\n" );
#endif
if( bytes_received > 2 )
{
bytes_received -= 2;
}
return( bytes_received );
}
/***********************************************************************
The following functions construct the required query into
a modbus query packet.
***********************************************************************/
#define REQUEST_QUERY_SIZE 6 /* the following packets require */
#define CHECKSUM_SIZE 2 /* 6 unsigned chars for the packet plus */
/* 2 for the checksum. */
void build_request_packet( int slave, int function, int start_addr,
int count, unsigned char *packet )
{
packet[ 0 ] = slave,
packet[ 1 ] = function;
start_addr -= 1;
packet[ 2 ] = start_addr >> 8;
packet[ 3 ] = start_addr & 0x00ff;
packet[ 4 ] = count >> 8;
packet[ 5 ] = count &0x00ff;
}
/************************************************************************
read_IO_status
read_coil_stat_query and read_coil_stat_response interigate
a modbus slave
- 1
- 2
- 3
- 4
- 5
前往页