From 82f531bc38e75ed3cc7bd5901f9f8080228d5af6 Mon Sep 17 00:00:00 2001 From: aphex999 Date: Fri, 25 Oct 2013 10:16:02 +0200 Subject: [PATCH 1/2] Addig support for using a pre-defined GPIO port for RTU RTS. Please check 'doc/modbus_rtu_set_rts_gpio.txt' for details. --- doc/modbus_rtu_set_rts_gpio.txt | 85 +++++++++++++++++++++ src/modbus-rtu-private.h | 1 + src/modbus-rtu.c | 128 ++++++++++++++++++++++++++++---- src/modbus-rtu.h | 3 + 4 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 doc/modbus_rtu_set_rts_gpio.txt diff --git a/doc/modbus_rtu_set_rts_gpio.txt b/doc/modbus_rtu_set_rts_gpio.txt new file mode 100644 index 000000000..e15dca08e --- /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()_ 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..dcd12e7c4 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,77 @@ 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; + } + + // 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) { 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 From 0a9f0ca5218f8a0d72529b6f12b67b1bbba186cf Mon Sep 17 00:00:00 2001 From: aphex999 Date: Fri, 25 Oct 2013 12:42:27 +0200 Subject: [PATCH 2/2] Bugfixing. --- doc/modbus_rtu_set_rts_gpio.txt | 2 +- src/modbus-rtu.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/modbus_rtu_set_rts_gpio.txt b/doc/modbus_rtu_set_rts_gpio.txt index e15dca08e..940ff3b2e 100644 --- a/doc/modbus_rtu_set_rts_gpio.txt +++ b/doc/modbus_rtu_set_rts_gpio.txt @@ -33,7 +33,7 @@ This function can only be used with a context using a RTU backend. RETURN VALUE ------------ -The _modbus_rtu_set_rts()_ function shall return 0 if successful. Otherwise it +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. diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index dcd12e7c4..570bbf473 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -1086,6 +1086,8 @@ int modbus_rtu_set_rts_gpio(modbus_t *ctx, int mode, char *gpio_directory) 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]; @@ -1215,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); }