diff --git a/doc/modbus_rtu_set_rts_gpio.txt b/doc/modbus_rtu_set_rts_gpio.txt new file mode 100644 index 000000000..940ff3b2e --- /dev/null +++ b/doc/modbus_rtu_set_rts_gpio.txt @@ -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 + diff --git a/src/modbus-rtu-private.h b/src/modbus-rtu-private.h index bec4a5348..785ac74bf 100644 --- a/src/modbus-rtu-private.h +++ b/src/modbus-rtu-private.h @@ -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; diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index 51f45aa26..570bbf473 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -275,6 +275,19 @@ 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) @@ -282,31 +295,49 @@ static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_lengt 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) @@ -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) { @@ -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); } diff --git a/src/modbus-rtu.h b/src/modbus-rtu.h index 7b125140a..39c6dd40a 100644 --- a/src/modbus-rtu.h +++ b/src/modbus-rtu.h @@ -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