#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "tcp.h"
#include "modbus_tcp.h"
#define MODBUS_TCP_PORT 502
#define LEN_NORMAL 6
#define READ_COIL_FC 0x01
#define READ_DISCRETE_INPUT_FC 0x02
#define READ_INPUT_REGISTER_FC 0x04
#define WRITE_COIL_FC 0x05
#define WRITE_SINGLE_REGISTER_FC 0x06
#define MAX_REQUEST_PACKET_LEN 20
#define MAX_RESPONSE_PACKET_LEN 300
#define MAX_RESPONSE_RESULT_LEN 300
#define MODBUS_TCP_DEFAULT_TIME_OUT 1000
#define OK 0
#define INVALID_INPUT 1
#define NET_ERR 2
#define ILLEGAL_FUNCTION 3
#define ILLEGAL_DATA_ADDR 4
#define ILLEGAL_DATA_VALUE 5
#define ILLEGAL_RESPONSE_LENGTH 6
#define ACKNOWLEDGE 7
#define SLAVE_DEVICE_BUSY 8
#define DENIED 9
#define MEMORY_PARITY_ERR 10
#define GATEWAY_PATH_UNABLE 11
#define GATEWAY_TARGET_DEV_FAIL 12
#define RESPONSE_ABNORMAL 13
#define TRUE 1
#define FAUSE 0
#define DEBUG 1
#if DEBUG
#define dprintf(format, args...) printf(format, ##args)
#else
#define dprintf(format, args...)
#endif
#pragma pack(1)
typedef struct {
uint8_t invalid[5];
uint8_t len;
uint8_t unit;
uint8_t fc;
} modbus_tcp_header_t;
static int read_common_modbus_tcp(int sockfd, uint8_t unit, \
uint8_t fc, uint16_t start_addr, uint16_t count, \
uint8_t *result, uint16_t *result_size);
static int get_request_packet_common(modbus_tcp_header_t modbus_header, \
uint16_t start_addr, uint16_t var, uint8_t *packet);
static int get_response_result_common(int sockfd, \
modbus_tcp_header_t modbus_header, uint16_t count, \
uint8_t *result, uint16_t *result_size);
static int get_response_result_read_input_register(int sockfd, \
modbus_tcp_header_t modbus_header, uint16_t count, \
uint16_t *result, uint16_t *result_size);
static int get_response_packet(int sockfd, \
modbus_tcp_header_t modbus_header, \
uint8_t *packet, uint16_t *packet_size);
static int get_response_result_write_coil(int sockfd, \
modbus_tcp_header_t modbus_header, \
uint16_t *address, uint16_t *state);
static int get_response_result_write_single_register(int sockfd, \
modbus_tcp_header_t modbus_header, \
uint16_t *address, uint16_t *value);
/*********************************************************
init_modbus_tcp()
Init the tcp modbus with the ip and get the
socket description.
***********************************************************/
int init_modbus_tcp(const char *ip, int *sockfd) {
if ((ip == NULL) || (sockfd == NULL)) {
return INVALID_INPUT;
}
int rc;
*sockfd = inittcpsock();
if (*sockfd < 0) {
return NET_ERR;
}
rc = clientconn(*sockfd, ip, MODBUS_TCP_PORT);
if (rc < 0) {
return NET_ERR;
}
return OK;
}
/*********************************************************
read_coil_modbus_tcp()
Read the boolean status of coils and set the array
elements in the result to TRUE or FALSE.
***********************************************************/
int read_coil_modbus_tcp(int sockfd, uint8_t unit, \
uint16_t start_addr, uint16_t count, uint8_t *result, \
uint16_t *result_size) {
int rc = read_common_modbus_tcp(sockfd, unit, \
READ_COIL_FC, start_addr, count, result, result_size);
return rc;
}
/*********************************************************
read_discrete_input_modbus_tcp()
Read the boolean status of discrete input and set the
array elements in the result to TRUE or FALSE.
***********************************************************/
int read_discrete_input_modbus_tcp(int sockfd, uint8_t unit, \
uint16_t start_addr, uint16_t count, uint8_t *result, \
uint16_t *result_size) {
int rc = read_common_modbus_tcp(sockfd, unit, \
READ_DISCRETE_INPUT_FC, start_addr, count, result, result_size);
return rc;
}
/*********************************************************
read_input_register_modbus_tcp()
Read the input registers and put the data
into an array.
***********************************************************/
int read_input_register_modbus_tcp(int sockfd, uint8_t unit, \
uint16_t start_addr, uint16_t count, uint16_t *result, \
uint16_t *result_size) {
if (sockfd < 0) {
return NET_ERR;
}
if ((result == NULL) || (result_size == NULL)) {
return INVALID_INPUT;
}
int rc;
int len;
uint8_t packet[MAX_REQUEST_PACKET_LEN];
modbus_tcp_header_t modbus_header;
memset(&modbus_header, 0, sizeof(modbus_header));
modbus_header.len = LEN_NORMAL;
modbus_header.unit = unit;
modbus_header.fc = READ_INPUT_REGISTER_FC;
len = get_request_packet_common(modbus_header, start_addr, \
count, packet);
#if DEBUG
int i;
for (i = 0; i < len; i++) {
dprintf("0x%x ", packet[i]);
}
dprintf("\n");
#endif
rc = clientwrite(sockfd, packet, len);
if (rc < 0) {
return NET_ERR;
}
rc = get_response_result_read_input_register(sockfd, \
modbus_header, count, result, result_size);
if (rc != OK) {
return rc;
}
return OK;
}
/*********************************************************
read_ndata_from_input_register_modbus_tcp()
Read the float data from input registers and put the data
into an array.
***********************************************************/
int read_ndata_from_input_register_modbus_tcp(int sockfd, \
uint8_t unit, uint16_t start_addr, uint16_t count, \
uint32_t *result, uint16_t *result_size) {
int rc;
int i, j = 0;
uint16_t tmp_result[MAX_RESPONSE_RESULT_LEN];
uint16_t tmp_result_size;
rc = read_input_register_modbus_tcp(sockfd, unit, \
start_addr, 2*count, tmp_result, &tmp_result_size);
if (rc != OK) {
return rc;
}
*result_size = tmp_result_size / 2;
for (i = 0; i < tmp_result_size; i += 2, j++) {
result[j] = tmp_result[i + 1];
result[j] = (result[j] << 16) + tmp_result[i];
}
return OK;
}
/*********************************************************
write_coil_modbus_tcp()
Set the coil state on or off.
***********************************************************/
int write_coil_modbus_tcp(int sockfd, uint8_t unit, \
uint16_t start_addr, uint16_t state) {
if (sockfd < 0) {
return NET_ERR;
}
int rc;
int len;
uint8_t packet[MAX_REQUEST_PACKET_LEN];
modbus_tcp_header_t modbus_header;
uint16_t state2 = state << 8;
uint16_t address;
uint16_t value;
memset(&modbus_header, 0, sizeof(modbus_header));
modbus_header.len = LEN_NORMAL;
modbus_header.unit = unit;
modbus_header.fc = WRITE_COIL_FC;
len = get_request_packet_common(modbus_header, start_addr, \
state2, packet);
#if DEBUG
int i;
for (i = 0; i < len; i++) {
dprintf("0x%x ", packet[i]);
}
dprintf("\n");
#endif
rc = clientwrite(sockfd, packet, len);
if (rc < 0) {
return NET_ERR;
}
rc = get_response_result_write_coil(sockfd, modbus_header, \
&address, &value);
if (rc != OK) {
return rc;
}
dprintf("address = 0x%x\n", address);
dprintf("state = 0x%x\n", value);
return OK;
}
/*********************************************************
write_single_register_modbus_tcp()
Set the single register with the value.
***********************************************************/
int write_single_register_mod