Compare commits

...

13 Commits

Author SHA1 Message Date
kpr0th 52bd932ac9
Merge 164accb10a into 68f988051c 2023-12-22 10:49:16 -05:00
kpr0th 164accb10a Additional adjustments to sync up README w/ -h output 2023-12-22 10:49:08 -05:00
kpr0th 5308e4ee93 Updated README
Sync'd up verbiage in the usage details with the usage summary.
2023-12-22 10:30:08 -05:00
kpr0th b76ac4bec4 Logging fix and enhancement
Corrected issue where loglevel in mbusd.conf wasn't parsed correctly. Added support for logfile in mbusd.conf.  Updated README and mbusd..conf.example to true-up with "-h" output and new logging options.
2023-12-22 10:26:43 -05:00
Victor Antonovich 68f988051c Rename `tty_(set|clr)_rts` to `tty_set_(tx|rx)` 2023-11-27 16:48:47 +03:00
Victor Antonovich b03d50b58c Improve documentation 2023-11-27 16:21:31 +03:00
Victor Antonovich 20c87df994 Add `rts_1` config value as an alias for `rts` 2023-11-27 15:43:34 +03:00
Cían Hughes 4ee7e8a834
Add new flag (-r) to support inverted RTS flow control\n\nThis is useful if you have a MAX485 connected to a Raspberry PI where you pull want RST low to transmit (#98) 2023-11-27 15:14:38 +03:00
Victor Antonovich b07681842f Add pyserial as a dependency for github workflow 2023-09-29 18:16:45 +03:00
Victor Antonovich b464ec9676 Update version to 0.5.2 and year to 2023 2023-09-29 18:09:00 +03:00
Victor Antonovich 6517dea57f Make use of Linux RS-485 support #97 2023-09-29 18:04:09 +03:00
Victor Antonovich 0f7a8c50fb Update .gitignore 2023-09-29 17:20:23 +03:00
Victor Antonovich 18fb6d086e Fix tests for changes in pymodbus API 2023-09-29 17:19:51 +03:00
16 changed files with 270 additions and 112 deletions

View File

@ -32,7 +32,7 @@ jobs:
- name: Install test dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twisted pymodbus
pip install setuptools wheel twisted pyserial pymodbus
sudo apt-get install -y socat
- name: Run tests

26
.gitignore vendored
View File

@ -1,25 +1,11 @@
/autom4te.cache
/config.cache
/config.h
/config.status
/.settings/
/src/.deps/
/src/*.o
/src/mbusd
/doc/mbusd.8
Makefile
/.settings
*.log
*.in~
systemd-units/mbusd@.service
*.conf
*.pyc
CMakeCache.txt
CMakeFiles
CPackConfig.cmake
CPackSourceConfig.cmake
cmake_install.cmake
.idea
*~
/.idea
/.vscode
.project
.cproject
build
.pydevproject
/build
build.sh

View File

@ -1,10 +1,11 @@
cmake_minimum_required(VERSION 3.2)
project(mbusd VERSION 0.5.1)
project(mbusd VERSION 0.5.2)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/extern_GPL)
include(CheckFunctionExists)
include(CheckLibraryExists)
include(CheckSymbolExists)
include(GNUInstallDirs)
include(FindUnixCommands)
include(FindSystemd)
@ -41,6 +42,10 @@ check_function_exists(cfsetispeed HAVE_CFSETISPEED)
if(HAVE_CFSETSPEED AND HAVE_CFSETISPEED)
add_definitions(-DHAVE_CFSETSPEED)
endif()
check_symbol_exists(TIOCSRS485 sys/ioctl.h HAVE_TIOCRS485)
if(HAVE_TIOCRS485)
add_definitions(-DHAVE_TIOCRS485)
endif()
check_function_exists(time HAVE_TIME)
check_function_exists(localtime HAVE_LOCALTIME)
if(HAVE_TIME AND HAVE_LOCALTIME)

View File

@ -1,4 +1,4 @@
Copyright (c) 2002-2003, 2013-2022 Victor Antonovich (v.antonovich@gmail.com)
Copyright (c) 2002-2003, 2013-2023 Victor Antonovich (v.antonovich@gmail.com)
Copyright (c) 2011 Andrew Denysenko <nitr0@seti.kr.ua>
All rights reserved.

View File

@ -52,19 +52,21 @@ can be altered in many ways, e.g. by using the following tools in the `build` di
Usage:
------
mbusd [-h] [-d] [-L logfile] [-v level] [-c cfgfile] [-p device] [-s speed] [-m mode]
[-t] [-y file] [-Y file] [-A address] [-P port] [-C maxconn] [-N retries]
[-R pause] [-W wait] [-T timeout]
mbusd [-h] [-d] [-L logfile] [-v level] [-c cfgfile]
[-p device] [-s speed] [-m mode] [-S]
[-t] [-r] [-y sysfsfile] [-Y sysfsfile]
[-A address] [-P port] [-C maxconn] [-N retries]
[-R pause] [-W wait] [-T timeout] [-b]
-h Usage help.
-d Instruct mbusd not to fork itself (non-daemonize).
-L logfile
Specifies log file name ('-' for logging to STDOUT only, default is /var/log/mbusd.log).
Specifies log file name ('-' for logging to STDOUT only, relative path or bare filename
will be stored at /var/log, default is /var/log/mbus.log).
-v level
Specifies log verbosity level (0 for errors only, 1 for warnings
and 2 for also information messages.) If mbusd was compiled in debug mode,
valid log levels are up to 9, where log levels above 2 forces
logging of information about additional internal events.
Specifies log verbosity level (0 for errors only, 1 for warnings and 2 for informational
messages also). If mbusd was compiled in debug mode, valid log levels are up to 9,
where log levels above 2 adds logging of information about additional internal events.
-c cfgfile
Read configuration from cfgfile.
-p device
@ -73,29 +75,31 @@ Usage:
Specifies serial port speed.
-m mode
Specifies serial port mode (like 8N1).
-t Enable RTS RS-485 data direction control (if not disabled while compile).
-y file
Enable RS-485 direction data direction control by writing '1' to file
for transmitter enable and '0' to file for transmitter disable
-Y file
Enable RS-485 direction data direction control by writing '0' to file
for transmitter enable and '1' to file for transmitter disable
-S Enable RS-485 support for given serial port device (Linux only)
-t Enable RTS RS-485 data direction control using RTS, active transmit.
-r Enable RTS RS-485 data direction control using RTS, active receive.
-y sysfsfile
Enable RS-485 direction data direction control by writing '1' to sysfs file
for transmitter enable and '0' to file for transmitter disable.
-Y sysfsfile
Enable RS-485 direction data direction control by writing '0' to sysfs file
for transmitter enable and '1' to file for transmitter disable.
-A address
Specifies TCP address to bind (default 0.0.0.0).
Specifies TCP server address to bind (default is 0.0.0.0).
-P port
Specifies TCP port number (default 502).
Specifies TCP server port number (default is 502).
-C maxconn
Specifies maximum number of simultaneous TCP connections.
Specifies maximum number of simultaneous TCP connections (default is 32).
-N retries
Specifies maximum number of request retries (0 disables retries).
Specifies maximum number of request retries (0 disables retries, default is 3).
-R pause
Specifies pause between requests in milliseconds.
Specifies pause between requests in milliseconds (default is 100ms).
-W wait
Specifies response wait time in milliseconds.
Specifies response wait time in milliseconds (default is 500ms).
-T timeout
Specifies connection timeout value in seconds (0 disables timeout).
Specifies connection timeout value in seconds (0 disables timeout, default is 60).
-b
Instructs **mbusd** to reply on a broadcast.
Instructs mbusd to reply on a broadcast.
Please note running **mbusd** on default Modbus TCP port (502) requires root privileges!

View File

@ -4,6 +4,14 @@
# #
#############################################
########## Logging settings #############
# Logging verbosity level
loglevel = 2
# Logfile (fully-qualified path, or filename [stored at /var/log/] or - for STDOUT only)
logfile = /var/log/mbus.log
########## Serial port settings #############
# Serial port device name
@ -15,7 +23,10 @@ speed = 9600
# Serial port mode
mode = 8n1
# RS-485 data direction control type (addc, rts, sysfs_0, sysfs_1)
# Enable RS-485 support for given serial port device (Linux only)
# enable_rs485 = no
# RS-485 data direction control type (addc, rts_0, rts/rts_1, sysfs_0, sysfs_1)
trx_control = addc
# Sysfs file to use to control data direction

View File

@ -18,10 +18,11 @@ mbusd \- MODBUS/TCP to MODBUS/RTU gateway.
.RB [ -m
.IR mode ]
.RB [ -t ]
.RB [ -r ]
.RB [ -y
.IR file ]
.IR sysfsfile ]
.RB [ -Y
.IR file ]
.IR sysfsfile ]
.RB [ -A
.IR address ]
.RB [ -P
@ -60,18 +61,22 @@ Specifies serial port device name.
Specifies serial port speed.
.IP "\fB-m \fImode\fR"
Specifies serial port mode (like 8N1).
.IP \fB-S\fR
Enable RS-485 support for given serial port device (Linux only).
.IP "\fB-A \fIaddress\fR"
Specifies TCP address to bind.
.IP "\fB-P \fIport\fR"
Specifies TCP port number.
.IP \fB-t\fR
Enable RTS RS-485 data direction control (if not disabled while compile).
Enable RTS RS-485 data direction control using RTS, active transmit.
.IP \fB-r\fR
Enable RTS RS-485 data direction control using RTS, active receive.
.IP "\fB-y \fIfile\fR"
Enable RS-485 direction data direction control by writing '1' to file
for transmitter enable and '0' to file for transmitter disable
for transmitter enable and '0' to file for transmitter disable.
.IP "\fB-Y \fIfile\fR"
Enable RS-485 direction data direction control by writing '0' to file
for transmitter enable and '1' to file for transmitter disable
for transmitter enable and '1' to file for transmitter disable.
.IP "\fB-C \fImaxconn\fR"
Specifies maximum number of simultaneous TCP connections.
.IP "\fB-N \fIretries\fR"

View File

@ -64,6 +64,9 @@ cfg_init(void)
strncpy(cfg.ttyport, DEFAULT_PORT, INTBUFSIZE);
cfg.ttyspeed = DEFAULT_SPEED;
strncpy(cfg.ttymode, DEFAULT_MODE, INTBUFSIZE);
#ifdef HAVE_TIOCRS485
cfg.rs485 = FALSE;
#endif
#ifdef TRXCTL
cfg.trxcntl = TRX_ADDC;
*cfg.trxcntl_file = '\0';
@ -95,6 +98,20 @@ cfg_ltrim(const char *s)
return (char *) s;
}
static int
cfg_is_empty(char *s)
{
char *p = s + strlen(s);
if (strlen(s) == 0)
return 1;
while (p > s && isspace((unsigned char )(*--p)))
{ //no-op
}
if (p == s && isspace((unsigned char )(*p)))
return 1;
return 0;
}
int
cfg_handle_param(char *name, char *value)
{
@ -183,6 +200,12 @@ cfg_handle_param(char *name, char *value)
cfg.conntimeout = strtoul(value, NULL, 0);
if (cfg.conntimeout > MAX_CONNTIMEOUT)
return 0;
#ifdef HAVE_TIOCRS485
}
else if (CFG_NAME_MATCH("enable_rs485"))
{
cfg.rs485 = CFG_VALUE_BOOL();
#endif
#ifdef TRXCTL
}
else if (CFG_NAME_MATCH("trx_control"))
@ -191,9 +214,13 @@ cfg_handle_param(char *name, char *value)
{
cfg.trxcntl = TRX_ADDC;
}
else if (CFG_VALUE_MATCH("rts"))
else if (CFG_VALUE_MATCH("rts") || CFG_VALUE_MATCH("rts_1"))
{
cfg.trxcntl = TRX_RTS;
cfg.trxcntl = TRX_RTS_1;
}
else if (CFG_VALUE_MATCH("rts_0"))
{
cfg.trxcntl = TRX_RTS_0;
}
else if (CFG_VALUE_MATCH("sysfs_0"))
{
@ -218,7 +245,43 @@ cfg_handle_param(char *name, char *value)
}
else if (CFG_NAME_MATCH("loglevel"))
{
cfg.dbglvl = (char)strtol(optarg, NULL, 0);
cfg.dbglvl = (char)strtol(value, NULL, 0);
# ifdef DEBUG
if (!(isdigit(*value)) || cfg.dbglvl < 0 || cfg.dbglvl > 9)
{ /* report about invalid log level */
CFG_ERR("invalid loglevel value: %s (must be 0-9)", value);
# else
if (!(isdigit(*value)) || cfg.dbglvl < 0 || cfg.dbglvl > 2)
{ /* report about invalid log level */
CFG_ERR("invalid loglevel value: %s (must be 0-2)", value);
# endif
return(0);
}
}
else if (CFG_NAME_MATCH("logfile"))
{
if (cfg_is_empty(value))
{
CFG_ERR("missing logfile value", value);
return(0);
}
else if (*value != '/')
{
if (*value == '-')
{
/* logging to file disabled */
*cfg.logname = '\0';
}
else
{ /* concatenate given log file name with default path */
strncpy(cfg.logname, LOGPATH, INTBUFSIZE);
strncat(cfg.logname, value, INTBUFSIZE - strlen(cfg.logname));
}
}
else strncpy(cfg.logname, value, INTBUFSIZE);
#endif
}
else {

View File

@ -54,10 +54,16 @@ typedef struct
int ttyspeed;
/* tty mode */
char ttymode[INTBUFSIZE + 1];
#ifdef HAVE_TIOCRS485
/* Linux RS-485 support use flag */
bool rs485;
#endif
#ifdef TRXCTL
/* trx control type (0 - ADDC, 1 - by RTS, 2 - by sysfs GPIO with 1 activating transmit, 3 - by sysfs GPIO with 0 activating transmit) */
int trxcntl;
/* trx control sysfs file */
char trxcntl_file[INTBUFSIZE + 1];
#endif
/* TCP server address */
char serveraddr[INTBUFSIZE + 1];
/* TCP server port number */

View File

@ -251,7 +251,7 @@ conn_write(int d, void *buf, size_t nbytes, int istty)
#ifdef TRXCTL
if (istty && cfg.trxcntl != TRX_ADDC)
{
tty_set_rts(d);
tty_set_tx(d);
tty_delay(35000000l/cfg.ttyspeed);
}
#endif
@ -267,7 +267,7 @@ conn_write(int d, void *buf, size_t nbytes, int istty)
if (istty && cfg.trxcntl != TRX_ADDC )
{
tty_delay(DV(nbytes, tty.bpc, cfg.ttyspeed));
tty_clr_rts(d);
tty_set_rx(d);
}
#endif

View File

@ -59,6 +59,7 @@
#include <netdb.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdbool.h>
#ifdef HAVE_LIBUTIL
# include <libutil.h>
#endif

View File

@ -56,6 +56,20 @@ ttydata_t tty;
/* Connections queue descriptor */
queue_t queue;
static int
main_is_empty(char *s)
{
char *p = s + strlen(s);
if (strlen(s) == 0)
return 1;
while (p > s && isspace((unsigned char )(*--p)))
{ //no-op
}
if (p == s && isspace((unsigned char )(*p)))
return 1;
return 0;
}
#ifndef HAVE_DAEMON
#include <fcntl.h>
#include <unistd.h>
@ -100,52 +114,62 @@ void
usage(char *exename)
{
cfg_init();
printf("%s-%s Copyright (C) 2002-2003, 2011, 2013-2022 Victor Antonovich <v.antonovich@gmail.com>, "
printf("%s-%s Copyright (C) 2002-2003, 2011, 2013-2023 Victor Antonovich <v.antonovich@gmail.com>, "
"Andrew Denysenko <nitr0@seti.kr.ua>\n\n"
"Usage: %s [-h] [-d] "
#ifdef LOG
"[-L logfile] [-v level] "
#endif
"[-c cfgfile] \n"
" [-p device] [-s speed] [-m mode]\n"
" [-p device] [-s speed] [-m mode]"
#ifdef HAVE_TIOCRS485
" [-S]"
#endif
"\n"
#ifdef TRXCTL
" [-t] [-y sysfsfile] [-Y sysfsfile]\n"
" [-t] [-r] [-y sysfsfile] [-Y sysfsfile]\n"
#endif
" [-A address] [-P port] [-C maxconn] [-N retries]\n"
" [-R pause] [-W wait] [-T timeout] [-b]\n\n"
"Options:\n"
" -h : this help\n"
" -d : don't daemonize\n"
" -d : don't fork (non-daemonize)\n"
#ifdef LOG
" -L logfile : set log file name (default %s%s, \n"
" -L logfile : set log file name (default is %s%s, \n"
" '-' for logging to STDOUT only)\n"
#ifdef DEBUG
" -v level : set log level (0-9, default %d, 0 - errors only)\n"
" -v level : set log level (0-9, default is %d, 0 - errors only)\n"
#else
" -v level : set log level (0-2, default %d, 0 - errors only)\n"
#endif
#endif
" -c cfgfile : read configuration from cfgfile\n"
" -p device : set serial port device name (default %s)\n"
" -s speed : set serial port speed (default %d)\n"
" -m mode : set serial port mode (default %s)\n"
" -A address : set TCP server address to bind (default %s)\n"
" -P port : set TCP server port number (default %d)\n"
" -p device : set serial port device name (default is %s)\n"
" -s speed : set serial port speed (default is %d)\n"
" -m mode : set serial port mode (default is %s)\n"
#ifdef HAVE_TIOCRS485
" -S : enable Linux RS-485 support for given serial port device\n"
#endif
#ifdef TRXCTL
" -t : enable RTS RS-485 data direction control using RTS\n"
" -t : enable RTS RS-485 data direction control using RTS, active transmit\n"
" -r : enable RTS RS-485 data direction control using RTS, active receive\n"
" -y : enable RTS RS-485 data direction control using sysfs file, active transmit\n"
" (writes '1' to sysfs file for transmit enable, '0' for transmit disable)\n"
" -Y : enable RTS RS-485 data direction control using sysfs file, active receive\n"
" (writes '0' to sysfs file for transmit enable, '1' for transmit disable)\n"
#endif
" -A address : set TCP server address to bind (default is %s)\n"
" -P port : set TCP server port number (default is %d)\n"
" -C maxconn : set maximum number of simultaneous TCP connections\n"
" (1-%d, default %d)\n"
" (1-%d, default is %d)\n"
" -N retries : set maximum number of request retries\n"
" (0-%d, default %d, 0 - without retries)\n"
" (0-%d, default is %d, 0 disables retrying)\n"
" -R pause : set pause between requests in milliseconds\n"
" (1-%d, default %lu)\n"
" (1-%d, default is %lu)\n"
" -W wait : set response wait time in milliseconds\n"
" (1-%d, default %lu)\n"
" (1-%d, default is %lu)\n"
" -T timeout : set connection timeout value in seconds\n"
" (0-%d, default %d, 0 - no timeout)\n"
" (0-%d, default is %d, 0 disables timeout)\n"
" -b : enable reply on broadcast"
"\n", PACKAGE, VERSION, exename,
#ifdef LOG
@ -159,6 +183,7 @@ usage(char *exename)
exit(0);
}
int
main(int argc, char *argv[])
{
@ -182,6 +207,9 @@ main(int argc, char *argv[])
#ifdef TRXCTL
"ty:Y:"
#endif
#ifdef HAVE_TIOCRS485
"S"
#endif
#ifdef LOG
"v:L:"
#endif
@ -208,36 +236,49 @@ main(int argc, char *argv[])
break;
#ifdef TRXCTL
case 't':
cfg.trxcntl = TRX_RTS;
cfg.trxcntl = TRX_RTS_1;
break;
case 'r':
cfg.trxcntl = TRX_RTS_0;
break;
case 'y':
cfg.trxcntl = TRX_SYSFS_1;
strncpy(cfg.trxcntl_file, optarg, INTBUFSIZE);
break;
break;
case 'Y':
cfg.trxcntl = TRX_SYSFS_0;
strncpy(cfg.trxcntl_file, optarg, INTBUFSIZE);
break;
break;
#endif
#ifdef HAVE_TIOCRS485
case 'S':
cfg.rs485 = TRUE;
break;
#endif
#ifdef LOG
case 'v':
cfg.dbglvl = (char)strtol(optarg, NULL, 0);
# ifdef DEBUG
if (cfg.dbglvl > 9)
if (!(isdigit(*optarg)) || cfg.dbglvl < 0 || cfg.dbglvl > 9)
{ /* report about invalid log level */
printf("%s: -v: invalid loglevel value"
" (%d, must be 0-9)\n", exename, cfg.dbglvl);
" (%s, must be 0-9)\n", exename, optarg);
# else
if (cfg.dbglvl < 0 || cfg.dbglvl > 9)
if (!(isdigit(*optarg)) || cfg.dbglvl < 0 || cfg.dbglvl > 2)
{ /* report about invalid log level */
printf("%s: -v: invalid loglevel value"
" (%d, must be 0-2)\n", exename, cfg.dbglvl);
" (%s, must be 0-2)\n", exename, optarg);
# endif
exit(-1);
}
break;
case 'L':
if (*optarg != '/')
if (main_is_empty(optarg))
{ /* report about invalid log file */
printf("%s: -L: missing logfile value\n", exename, optarg);
exit(-1);
}
else if (*optarg != '/')
{
if (*optarg == '-')
{

View File

@ -65,6 +65,9 @@ tty_init(ttydata_t *mod)
{
mod->bpc++;
}
#ifdef HAVE_TIOCRS485
mod->rs485 = cfg.rs485;
#endif
#ifdef TRXCTL
mod->trxcntl = cfg.trxcntl;
#endif
@ -186,7 +189,26 @@ tty_set_attr(ttydata_t *mod)
return RC_ERR;
tcflush(mod->fd, TCIOFLUSH);
#ifdef TRXCTL
tty_clr_rts(mod->fd);
tty_set_rx(mod->fd);
#endif
#ifdef HAVE_TIOCRS485
if (mod->rs485)
{
struct serial_rs485 rs485conf;
#ifdef LOG
logw(2, "tty: trying to enable RS-485 support for %s", mod->port);
#endif
if (ioctl(mod->fd, TIOCGRS485, &rs485conf) < 0) {
return RC_ERR;
}
rs485conf.flags |= SER_RS485_ENABLED;
if (ioctl(mod->fd, TIOCSRS485, &rs485conf) < 0) {
return RC_ERR;
}
#ifdef LOG
logw(2, "tty: enabled RS-485 support for %s", mod->port);
#endif
}
#endif
flag = fcntl(mod->fd, F_GETFL, 0);
if (flag < 0)
@ -443,27 +465,33 @@ void sysfs_gpio_set(char *filename, char *value) {
}
/* Set RTS line to active state */
/* Set tty device into transmit mode */
void
tty_set_rts(int fd)
tty_set_tx(int fd)
{
if ( TRX_RTS == cfg.trxcntl ) {
if ( TRX_RTS_1 == cfg.trxcntl ) {
int mstat = TIOCM_RTS;
ioctl(fd, TIOCMBIS, &mstat);
} else if ( TRX_SYSFS_1 == cfg.trxcntl) {
} else if ( TRX_RTS_0 == cfg.trxcntl ) {
int mstat = TIOCM_RTS;
ioctl(fd, TIOCMBIC, &mstat);
} else if ( TRX_SYSFS_1 == cfg.trxcntl) {
sysfs_gpio_set(cfg.trxcntl_file,"1");
} else if ( TRX_SYSFS_0 == cfg.trxcntl) {
sysfs_gpio_set(cfg.trxcntl_file,"0");
}
}
/* Set RTS line to passive state */
/* Set tty device into receive mode */
void
tty_clr_rts(int fd)
tty_set_rx(int fd)
{
if ( TRX_RTS == cfg.trxcntl ) {
if ( TRX_RTS_1 == cfg.trxcntl ) {
int mstat = TIOCM_RTS;
ioctl(fd, TIOCMBIC, &mstat);
} else if ( TRX_RTS_0 == cfg.trxcntl ) {
int mstat = TIOCM_RTS;
ioctl(fd, TIOCMBIS, &mstat);
} else if ( TRX_SYSFS_1 == cfg.trxcntl) {
sysfs_gpio_set(cfg.trxcntl_file,"0");
} else if ( TRX_SYSFS_0 == cfg.trxcntl) {

View File

@ -34,6 +34,10 @@
#ifndef _TTY_H
#define _TTY_H
#ifdef HAVE_TIOCRS485
#include <linux/serial.h>
#endif
#include "globals.h"
#include "cfg.h"
@ -73,9 +77,10 @@
*/
#ifdef TRXCTL
#define TRX_ADDC 0
#define TRX_RTS 1
#define TRX_SYSFS_1 2
#define TRX_SYSFS_0 3
#define TRX_RTS_1 1
#define TRX_RTS_0 2
#define TRX_SYSFS_1 3
#define TRX_SYSFS_0 4
#endif
/*
@ -96,6 +101,9 @@ typedef struct
int speed; /* serial port speed */
char *port; /* serial port device name */
int bpc; /* bits per character */
#ifdef HAVE_TIOCRS485
bool rs485; /* use Linux RS-485 support flag */
#endif
#ifdef TRXCTL
int trxcntl; /* trx control type (enum - see values in config.h) */
#endif
@ -120,8 +128,8 @@ int tty_set_attr(ttydata_t *mod);
speed_t tty_transpeed(int speed);
int tty_cooked(ttydata_t *mod);
int tty_close(ttydata_t *mod);
void tty_set_rts(int fd);
void tty_clr_rts(int fd);
void tty_set_tx(int fd);
void tty_set_rx(int fd);
void tty_delay(int usec);
#endif /* _TTY_H */

View File

@ -14,7 +14,7 @@ import logging
#---------------------------------------------------------------------------#
#from pymodbus.server.async import StartTcpServer
#from pymodbus.server.async import StartUdpServer
from pymodbus.server.asynchronous import StartSerialServer
from pymodbus.server import StartSerialServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
@ -92,7 +92,7 @@ class ModbusSerialServer:
hr = ModbusSequentialDataBlock(0, [0]*8), # holding regs
ir = ModbusSequentialDataBlock(0, list(range(8))), # input regs
zero_mode=True) # request(0-7) will map to the address (0-7)
context = ModbusServerContext(slaves=store, single=True)
context = ModbusServerContext(slaves={1: store}, single=False)
#---------------------------------------------------------------------------#
# initialize the server information
@ -112,7 +112,7 @@ class ModbusSerialServer:
#---------------------------------------------------------------------------#
#StartTcpServer(context, identity=identity, address=("localhost", 5020))
#StartUdpServer(context, identity=identity, address=("localhost", 502))
StartSerialServer(context, identity=identity, port=self.serialPort, baudrate=19200, framer=framer, broadcast_enable=True)
StartSerialServer(context=context, identity=identity, port=self.serialPort, baudrate=19200, framer=framer, broadcast_enable=True)
#StartSerialServer(context, identity=identity, port='/dev/pts/3', framer=ModbusAsciiFramer)
p = None

View File

@ -8,7 +8,7 @@ from subprocess import Popen, PIPE, STDOUT
from os.path import isfile
from time import sleep
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.client import ModbusTcpClient
from pymodbus.pdu import ExceptionResponse
from pymodbus.bit_read_message import ReadDiscreteInputsResponse, ReadCoilsResponse
from pymodbus.bit_write_message import WriteMultipleCoilsResponse, WriteSingleCoilResponse
@ -62,35 +62,35 @@ class TestModbusRequests(unittest.TestCase):
bits = [random.randrange(2)>0 for i in range(8)]
# 15 Write Multiple Coils
result = self.client.write_coils(0, bits, unit=1)
result = self.client.write_coils(0, bits, slave=1)
self.assertIsInstance(result, WriteMultipleCoilsResponse, result)
self.assertEqual(result.address, 0, result)
self.assertEqual(result.count, 8, result)
# 01 Read Coils
result = self.client.read_coils(0, 8, unit=1)
result = self.client.read_coils(0, 8, slave=1)
self.assertIsInstance(result, ReadCoilsResponse, result)
self.assertEqual(result.bits, bits, result)
# 05 Write Single Coil
bit1 = not bits[0]
result = self.client.write_coil(0, bit1, unit=1)
result = self.client.write_coil(0, bit1, slave=1)
self.assertIsInstance(result, WriteSingleCoilResponse, result)
self.assertEqual(result.address, 0, result)
self.assertEqual(result.value, bit1, result)
result = self.client.read_coils(0, 1, unit=1)
result = self.client.read_coils(0, 1, slave=1)
self.assertIsInstance(result, ReadCoilsResponse, result)
self.assertEqual(result.bits[0], bit1, result)
def test_discreteInputs(self):
# 02 Read Discrete Inputs
result = self.client.read_discrete_inputs(0, 8, unit=1)
result = self.client.read_discrete_inputs(0, 8, slave=1)
self.assertIsInstance(result, ReadDiscreteInputsResponse, result)
self.assertEqual(result.bits, [True]*8, result)
def test_inputRegisters(self):
# 04 Read Input Registers
result = self.client.read_input_registers(0, 8, unit=1)
result = self.client.read_input_registers(0, 8, slave=1)
self.assertIsInstance(result, ReadInputRegistersResponse, result)
self.assertEqual(result.registers, list(range(8)), result)
@ -98,28 +98,28 @@ class TestModbusRequests(unittest.TestCase):
registers = [random.randrange(8) for i in range(8)]
# 16 Write Multiple Holding Registers
result = self.client.write_registers(0, registers, unit=1)
result = self.client.write_registers(0, registers, slave=1)
self.assertIsInstance(result, WriteMultipleRegistersResponse, result)
self.assertEqual(result.address, 0, result)
self.assertEqual(result.count, 8, result)
# 03 Read Multiple Holding Registers
result = self.client.read_holding_registers(0, 8, unit=1)
result = self.client.read_holding_registers(0, 8, slave=1)
self.assertIsInstance(result, ReadHoldingRegistersResponse, result)
self.assertEqual(result.registers, registers, result)
# 06 Write Single Holding Register
register1 = (registers[0] + 1) % 65535
result = self.client.write_register(0, register1, unit=1)
result = self.client.write_register(0, register1, slave=1)
self.assertIsInstance(result, WriteSingleRegisterResponse, result)
self.assertEqual(result.address, 0, result)
self.assertEqual(result.value, register1, result)
result = self.client.read_holding_registers(0, 1, unit=1)
result = self.client.read_holding_registers(0, 1, slave=1)
self.assertIsInstance(result, ReadHoldingRegistersResponse, result)
self.assertEqual(result.registers[0], register1, result)
def test_exception(self):
result = self.client.write_coil(1000, False, unit=1) # invalid address 1000
result = self.client.write_coil(1000, False, slave=1) # invalid address 1000
self.assertIsInstance(result, ExceptionResponse, result)
self.assertEqual(result.original_code, 5, result) # fc05 Write Single Coil
self.assertEqual(result.exception_code, 2, result) # Illegal Data Address
@ -127,10 +127,10 @@ class TestModbusRequests(unittest.TestCase):
def test_broadcast(self):
registers = [random.randrange(8) for i in range(8)]
# 16 Write Multiple Holding Registers
result = self.client.write_registers(0, registers, unit=0)
result = self.client.write_registers(0, registers, slave=0)
# 03 Read Multiple Holding Registers
result = self.client.read_holding_registers(0, 8, unit=1)
result = self.client.read_holding_registers(0, 8, slave=1)
self.assertIsInstance(result, ReadHoldingRegistersResponse, result)
self.assertEqual(result.registers, registers, result)