Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions doc/modbus_rtu_set_rts_gpio.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
modbus_rtu_set_rts_gpio(3)
==========================


NAME
----
modbus_rtu_set_rts_gpio - set the RTS mode in RTU using a GPIO port


SYNOPSIS
--------
*int modbus_rtu_set_rts(modbus_t *'ctx', int 'mode', char *'gpio_directory')*


DESCRIPTION
-----------
The _modbus_rtu_set_rts_gpio()_ function shall set the Request To Send mode to
communicate on a RS485 serial bus. By default, the mode is set to
MODBUS_RTU_RTS_NONE and no signal is issued before writing data on the wire.

The function first checks, if the given GPIO port exits, has it's 'direction' set
to 'out', and if 'value' can be written by the current user.

To enable the RTS mode, the values MODBUS_RTU_RTS_GPIO_UP or MODBUS_RTU_RTS_GPIO_DOWN
shall be used, these modes enable the RTS mode and set the polarity at the same
time. When MODBUS_RTU_RTS_GPIO_UP is used, a '1' character will be written to file
'value' at the given 'gpio_directory' for enabling write mode, and '0' for disabling.
The MODBUS_RTU_RTS_GPIO_DOWN mode applies the same procedure but with an inversed
RTS flag.

This function can only be used with a context using a RTU backend.


RETURN VALUE
------------
The _modbus_rtu_set_rts_gpio()_ function shall return 0 if successful. Otherwise it
shall return -1 and set errno to one of the values defined below.


ERRORS
------
*EINVAL*::
The libmodbus backend isn't RTU, the mode given in argument is invalid, or
the given directory for GPIO does not exits or user lacks on permissions.


EXAMPLE
-------
.Enable the RTS mode with positive polarity
[source,c]
-------------------
modbus_t *ctx;
uint16_t tab_reg[10];

ctx = modbus_new_rtu("/dev/ttyS0", 115200, 'N', 8, 1);
modbus_set_slave(ctx, 1);
modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
char gpio_directory[] = "/sys/class/gpio/gpio20";
modbus_rtu_set_rts_gpio(mb, MODBUS_RTU_RTS_GPIO_UP, gpio_directory);

if (modbus_connect(ctx) == -1) {
fprintf(stderr, "Connexion failed: %s\n", modbus_strerror(errno));
modbus_free(ctx);
return -1;
}

rc = modbus_read_registers(ctx, 0, 7, tab_reg);
if (rc == -1) {
fprintf(stderr, "%s\n", modbus_strerror(errno));
return -1;
}

modbus_close(ctx);
modbus_free(ctx);
-------------------

SEE ALSO
--------
linkmb:modbus_rtu_get_rts[3]


AUTHORS
-------
The libmodbus documentation was written by Stéphane Raimbault
<stephane.raimbault@gmail.com>
1 change: 1 addition & 0 deletions src/modbus-rtu-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ typedef struct _modbus_rtu {
#if HAVE_DECL_TIOCM_RTS
int rts;
int onebyte_time;
char* gpio_value_filename;
#endif
/* To handle many slaves on the same link */
int confirmation_to_ignore;
Expand Down
131 changes: 118 additions & 13 deletions src/modbus-rtu.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,38 +275,69 @@ static void _modbus_rtu_ioctl_rts(int fd, int on)
}
#endif

static void _modbus_rtu_ioctl_rts_gpio(char *gpio_value_filename, int on){

// File IO error checks @ modbus_rtu_set_rts_gpio
// (GPIO port should not be unexported)

FILE *fp;
fp = fopen(gpio_value_filename, "w");
fprintf(fp, "%d", on);
fflush(fp);
fclose(fp);

}

static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
{
#if defined(_WIN32)
modbus_rtu_t *ctx_rtu = ctx->backend_data;
DWORD n_bytes = 0;
return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) ? n_bytes : -1;
#else
#if HAVE_DECL_TIOCM_RTS

modbus_rtu_t *ctx_rtu = ctx->backend_data;
if (ctx_rtu->rts != MODBUS_RTU_RTS_NONE) {
ssize_t size;

if (ctx_rtu->rts == MODBUS_RTU_RTS_NONE){

return write(ctx->s, req, req_length);

} else {

ssize_t size = 0;

#if HAVE_DECL_TIOCM_RTS
if (ctx->debug) {
fprintf(stderr, "Sending request using RTS signal\n");
}

_modbus_rtu_ioctl_rts(ctx->s, ctx_rtu->rts == MODBUS_RTU_RTS_UP);
usleep(_MODBUS_RTU_TIME_BETWEEN_RTS_SWITCH);
if(ctx_rtu->rts == MODBUS_RTU_RTS_UP || ctx_rtu->rts == MODBUS_RTU_RTS_DOWN){

_modbus_rtu_ioctl_rts(ctx->s, ctx_rtu->rts == MODBUS_RTU_RTS_UP);
usleep(_MODBUS_RTU_TIME_BETWEEN_RTS_SWITCH);

size = write(ctx->s, req, req_length);
size = write(ctx->s, req, req_length);

usleep(ctx_rtu->onebyte_time * req_length + _MODBUS_RTU_TIME_BETWEEN_RTS_SWITCH);
_modbus_rtu_ioctl_rts(ctx->s, ctx_rtu->rts != MODBUS_RTU_RTS_UP);

} else if (ctx_rtu->rts == MODBUS_RTU_RTS_GPIO_UP || ctx_rtu->rts == MODBUS_RTU_RTS_GPIO_DOWN){

_modbus_rtu_ioctl_rts_gpio(ctx_rtu->gpio_value_filename, ctx_rtu->rts == MODBUS_RTU_RTS_GPIO_UP);
usleep(_MODBUS_RTU_TIME_BETWEEN_RTS_SWITCH);

size = write(ctx->s, req, req_length);

usleep(ctx_rtu->onebyte_time * req_length + _MODBUS_RTU_TIME_BETWEEN_RTS_SWITCH);
_modbus_rtu_ioctl_rts_gpio(ctx_rtu->gpio_value_filename, ctx_rtu->rts != MODBUS_RTU_RTS_GPIO_UP);

}
#endif

usleep(ctx_rtu->onebyte_time * req_length + _MODBUS_RTU_TIME_BETWEEN_RTS_SWITCH);
_modbus_rtu_ioctl_rts(ctx->s, ctx_rtu->rts != MODBUS_RTU_RTS_UP);

return size;
} else {
#endif
return write(ctx->s, req, req_length);
#if HAVE_DECL_TIOCM_RTS
}
#endif
#endif
}

static int _modbus_rtu_receive(modbus_t *ctx, uint8_t *req)
Expand Down Expand Up @@ -1012,6 +1043,79 @@ int modbus_rtu_set_rts(modbus_t *ctx, int mode)
return -1;
}

int modbus_rtu_set_rts_gpio(modbus_t *ctx, int mode, char *gpio_directory)
{

if (ctx == NULL) {
fprintf(stderr, "CTX is null.\n");
errno = EINVAL;
return -1;
}

if (ctx->backend->backend_type != _MODBUS_BACKEND_TYPE_RTU) {
fprintf(stderr, "Backend must be RTU.\n");
errno = EINVAL;
return -1;
}

modbus_rtu_t *ctx_rtu = ctx->backend_data;

if (mode != MODBUS_RTU_RTS_GPIO_UP && mode != MODBUS_RTU_RTS_GPIO_DOWN) {
fprintf(stderr, "Please select a GPIO RTS mode.\n");
errno = EINVAL;
return -1;
} else {
ctx_rtu->rts = mode;
}

// Set filenames
char *gpio_direction_filename;
gpio_direction_filename = (char *) malloc((strlen(gpio_directory) + 12) * sizeof(char));
strcat(gpio_direction_filename, gpio_directory);
strcat(gpio_direction_filename, "/direction");

ctx_rtu->gpio_value_filename = (char *) malloc((strlen(gpio_directory) + 8) * sizeof(char));
strcat(ctx_rtu->gpio_value_filename, gpio_directory);
strcat(ctx_rtu->gpio_value_filename, "/value");

// Check GPIO direction-file permissions
errno = 0;
FILE *fp;
fp = fopen(gpio_direction_filename, "r");
if (errno != 0){
fprintf(stderr, "Could not open \"%s\" for reading, errno: %d.\n", gpio_direction_filename, errno);
return -1;
}

free(gpio_direction_filename);

// Get GPIO port direction
char buff[4];
fgets(buff, 4, fp);
fclose(fp);

if(strcmp(buff, "out") != 0){
fprintf(stderr, "GPIO direction is not \"out\".\n");
errno = EINVAL;
return -1;
}

// Check GPIO value-file permissions
fp = fopen(ctx_rtu->gpio_value_filename, "w");
if (errno != 0){
fprintf(stderr, "Could not open \"%s\" for writing, errno: %d.\n", ctx_rtu->gpio_value_filename, errno);
return -1;
}
fclose(fp);

// Set the RTS port in order to not reserve the RS485 bus
_modbus_rtu_ioctl_rts_gpio(ctx_rtu->gpio_value_filename, ctx_rtu->rts != MODBUS_RTU_RTS_GPIO_UP);


return 0;

}

int modbus_rtu_get_rts(modbus_t *ctx)
{
if (ctx == NULL) {
Expand Down Expand Up @@ -1113,6 +1217,7 @@ static int _modbus_rtu_select(modbus_t *ctx, fd_set *rset,

static void _modbus_rtu_free(modbus_t *ctx) {
free(((modbus_rtu_t*)ctx->backend_data)->device);
free(((modbus_rtu_t*)ctx->backend_data)->gpio_value_filename);
free(ctx->backend_data);
free(ctx);
}
Expand Down
3 changes: 3 additions & 0 deletions src/modbus-rtu.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx);
#define MODBUS_RTU_RTS_NONE 0
#define MODBUS_RTU_RTS_UP 1
#define MODBUS_RTU_RTS_DOWN 2
#define MODBUS_RTU_RTS_GPIO_UP 3
#define MODBUS_RTU_RTS_GPIO_DOWN 4

MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode);
MODBUS_API int modbus_rtu_set_rts_gpio(modbus_t *ctx, int mode, char *gpio_directory);
MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx);

MODBUS_END_DECLS
Expand Down