📄 modbus_rtu.c
字号:
/* 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 thanstring_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 to get coil status. An array of coils shall be set to TRUE or FALSE according to the response from the slave. *************************************************************************/int read_IO_status( int function, int slave, int start_addr, int count, int *dest, int dest_size, int ttyfd ){ /* local declaration */ int read_IO_stat_response( int *dest, int dest_size, int coil_count, unsigned char *query, int fd ); int status; unsigned char packet[ REQUEST_QUERY_SIZE + CHECKSUM_SIZE ]; build_request_packet( slave, function, start_addr, count, packet ); if( send_query( ttyfd, packet, REQUEST_QUERY_SIZE ) > -1 ) { status = read_IO_stat_response( dest, dest_size, count, packet, ttyfd ); } else { status = PORT_FAILURE; } return( status );}/************************************************************************ read_coil_status reads the boolean status of coils and sets the array elements in the destination to TRUE or FALSE*************************************************************************/int read_coil_status( int slave, int start_addr, int count, int *dest, int dest_size, int ttyfd ){ int function = 0x01; int status; status = read_IO_status( function, slave, start_addr, count, dest, dest_size, ttyfd ); return( status );}/************************************************************************ read_input_status same as read_coil_status but reads the slaves input table.************************************************************************/int read_input_status( int slave, int start_addr, int count, int *dest, int dest_size, int ttyfd ){ int function = 0x02; /* Function: Read Input Status */ int status; status = read_IO_status( function, slave, start_addr, count, dest, dest_size, ttyfd ); return( status );}/************************************************************************** read_IO_stat_response this function does the work of setting array elements to TRUE or FALSE.**************************************************************************/int read_IO_stat_response( int *dest, int dest_size, int coil_count, unsigned char *query, int fd ){ unsigned char data[ MAX_RESPONSE_LENGTH ]; int raw_response_length; int temp, i, bit, dest_pos = 0; int coils_processed = 0; raw_response_length = modbus_response( data, query, fd ); if( raw_response_length > 0 ) { for( i = 0; i < ( data[2] ) && i < dest_size; i++ ) { /* shift reg hi_byte to temp */ temp = data[ 3 + i ] ; for( bit = 0x01; bit & 0xff && coils_processed < coil_count; ) { if( temp & bit ) { dest[ dest_pos ] = TRUE; } else { dest[ dest_pos ] = FALSE; } coils_processed++; dest_pos++; bit = bit << 1; } } } return( raw_response_length );}/************************************************************************ read_registers read the data from a modbus slave and put that data into an array.************************************************************************/int read_registers( int function, int slave, int start_addr, int count, int *dest, int dest_size, int ttyfd ){ /* local declaration */ int read_reg_response( int *dest, int dest_size, unsigned char *query, int fd ); int status; unsigned char packet[ REQUEST_QUERY_SIZE + CHECKSUM_SIZE ]; build_request_packet( slave, function, start_addr, count, packet ); if( send_query( ttyfd, packet, REQUEST_QUERY_SIZE ) > -1 ) { status = read_reg_response( dest, dest_size, packet, ttyfd ); } else { status = PORT_FAILURE; } return( status );}/************************************************************************ read_holding_registers Read the holding registers in a slave and put the data into an array.*************************************************************************/int read_holding_registers( int slave, int start_addr, int count, int *dest, int dest_size, int ttyfd ){ int function = 0x03; /* Function: Read Holding Registers */ int status; if( count > MAX_READ_REGS ) { count = MAX_READ_REGS;#ifdef DEBUG fprintf( stderr, "Too many registers requested.\n" );#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -