def main(): print('Single R421A08 relay board example\n') # Create relay_modbus object _modbus = relay_modbus.Modbus(serial_port=SERIAL_PORT, verbose=False) # Open serial port try: _modbus.open() except relay_modbus.SerialOpenException as err: print(err) sys.exit(1) # Create relay board object board = relay_boards.R421A08(_modbus, address=address, board_name=board_name, verbose=False) # Print board info print_relay_board_info(board) # Control some relays relay_control(board) print('Done')
def test_crc(self): modbus_test = relay_modbus.Modbus(self._serial_port, verbose=False) modbus_test.open() self.assertListEqual(modbus_test.crc([]), [0xFF, 0xFF]) self.assertListEqual(modbus_test.crc([0x00]), [0xBF, 0x40]) self.assertListEqual(modbus_test.crc([0x12, 0x34, 0x56, 0x67]), [0x3A, 0xD8]) self.assertListEqual(modbus_test.crc([0x12, 0x34, 0x56, 0x67, 0x3A, 0xD8]), [0x00, 0x00])
def test_send_verbose(self): modbus_verbose = relay_modbus.Modbus(self._serial_port, verbose=True) modbus_verbose.open() with captured_output() as (out, err): modbus_verbose.send([0x01, 0x06, 0x00, 0x01, 0x01, 0x00]) output = out.getvalue().strip() self.assertEqual(output, 'TX 8 Bytes: 01 06 00 01 01 00 D9 9A')
def main(): print('Multiple R421A08 relay boards example\n') # ---------------------------------------------------------------------------------------------- # Create relay_modbus object _modbus = relay_modbus.Modbus(serial_port=SERIAL_PORT, verbose=False) # Open serial port try: _modbus.open() except relay_modbus.SerialOpenException as err: print(err) sys.exit(1) # ---------------------------------------------------------------------------------------------- # Create relay board object with on address 1 relays_kitchen = relay_boards.R421A08(_modbus, address=1, board_name='Kitchen') # Create second relay board object on address 2 relays_living_room = relay_boards.R421A08(_modbus, address=1, board_name='Living room') # ---------------------------------------------------------------------------------------------- # Print board info print_relay_board_info(relays_kitchen) print_relay_board_info(relays_living_room) # ---------------------------------------------------------------------------------------------- # Control relays kitchen print('Relay board #{} relay 1 on'.format(relays_kitchen.address)) relays_kitchen.on(1) print('Relay board #{} relays 2, 3 and 7 on'.format( relays_kitchen.address)) relays_kitchen.on_multi([2, 3, 7]) print('Relay board #{} status:'.format(relays_kitchen.address)) relays_kitchen.print_status_all() time.sleep(2) # ---------------------------------------------------------------------------------------------- # Control relays living room print('Relay board #{} toggle relay 8'.format(relays_living_room.address)) relays_living_room.toggle(8) print('Relay board #{} status:'.format(relays_kitchen.address)) relays_living_room.print_status_all() time.sleep(2) # ---------------------------------------------------------------------------------------------- # Control relays kitchen and living room print('Relay board #{} all off'.format(relays_kitchen.address)) relays_kitchen.off_all() print('Relay board #{} all off'.format(relays_living_room.address)) relays_living_room.off_all() print('Done')
def test_send_receive(self): modbus_test = relay_modbus.Modbus(self._serial_port, verbose=False) modbus_test.open() tx_data = [0x01, 0x06, 0x00, 0x01, 0x01, 0x00] modbus_test.send(tx_data) rx_data = modbus_test.receive(len(tx_data)) self.assertListEqual(tx_data, rx_data[:len(tx_data)])
def test_send_receive_no_append_crc(self): modbus_test = relay_modbus.Modbus(self._serial_port, verbose=False) modbus_test.open() tx_data = [0x01, 0x06, 0x00, 0x01, 0x02, 0x00, 0xD9, 0x6A] modbus_test.send(tx_data, append_crc_to_frame=False) rx_data = modbus_test.receive(len(tx_data)) self.assertListEqual(tx_data, rx_data)
def test_transfer_timeout(self): modbus_test = relay_modbus.Modbus(self._serial_port, verbose=False) modbus_test.open() with captured_output() as (out_, err_): errmsg = None try: modbus_test.transfer([0x01], rx_length=100) except relay_modbus.TransferException as err: errmsg = str(err) self.assertEqual(errmsg, 'RX error: Receive timeout')
def modbus_cmd_monitor(args): """ MODBUS monitor command: Print receiving frames on the serial console. :param args: Commandline arguments :return: None """ # Create MODBUS object _modbus = relay_modbus.Modbus(args.serial_port, baud_rate=args.baudrate) _modbus.open() # Start monitoring _modbus.monitor_start(args.address)
def test_monitor(self): modbus_send = relay_modbus.Modbus(self._serial_port) modbus_monitor = relay_modbus.Modbus(self._serial_port_monitor) modbus_send.open() modbus_monitor.open() for address in [0, self._address]: with captured_output() as (out, err): # Start monitor modbus_monitor.monitor_start(address, blocking=False) # Send message modbus_send.send([0x01, 0x06, 0x00, 0x01, 0x01, 0x00]) # Wait some time to process the frame time.sleep(0.1) # Stop monitor modbus_monitor.monitor_stop() if address == 0: msg_start = 'Monitor receiving frames from all addresses.' else: msg_start = 'Monitor receiving frames from address {} only.'.format(address) # Check stdout output = out.getvalue().strip() self.assertEqual(output, '{}\n' 'Press CTRL+C to abort.\n' 'RX 16 Bytes: 01 06 00 01 01 00 D9 9A 01 06 00 01 01 00 D9 9A'. format(msg_start)) # Wait for test completion time.sleep(0.05)
def modbus_cmd_send(args): """ MODBUS send command :param args: Commandline arguments :return: None """ # Create MODBUS object _modbus = relay_modbus.Modbus(args.serial_port, baud_rate=args.baudrate, verbose=True) try: _modbus.open() except relay_modbus.SerialOpenException: print_stderr('Error: Cannot open serial port: ' + args.serial_port) sys.exit(1) tx_data = None try: tx_str = args.frame # Replace characters, for example: # ':010600010100D99A' -> '010600010100D99A' # '0x01, 0x06, 0x00, 0x01, 0x01, 0x00, 0xD9, 0x9A' -> '01 06 00 01 01 00 D9 9A' for c in ['0x', ':', ',', ' ']: tx_str = tx_str.replace(c, '') # Insert space every 2 characters, for example: # '010600010100D99A' -> '01 06 00 01 01 00 D9 9A' tx_str = ' '.join(a + b for a, b in zip(tx_str[::2], tx_str[1::2])) # Split data in ints, for example: # [0x01, 0x06, 0x00, 0x01, 0x01, 0x00, 0xD9, 0x9A] tx_data = [int(i, 16) for i in tx_str.split(' ')] except ValueError: print('Incorrect send argument. Expecting --send formats like:') print(' ":010600010100"') print(' "01 06 00 01 01 00"') print(' "0x01, 0x06, 0x00, 0x01, 0x01, 0x00"') print('Note: CRC bytes can be omitted.') sys.exit(1) # Send and receive MODBUS frame _modbus.transfer(tx_data, not args.no_append_crc, rx_length=0)
def __init__(self, parent, settings_file=None): frmRelays.__init__(self, parent) # Protect events when closing window self.closing_window = False # Set window size and minimum window size self.SetSize(wx.Size(580, 650)) self.SetMinSize(wx.Size(580, 650)) # Set application icon ico_path = resource_path('images/relay.ico') if ico_path: self.m_relay_icon = wx.Icon(ico_path) self.SetIcon(self.m_relay_icon) self.SetTitle('R421A08 Relay Control') # Create taskbar icon self.m_taskbar_icon = CustomTaskBarIcon(self, self.m_relay_icon) # Create MODBUS object self.m_relay_modbus = relay_modbus.Modbus() # Refresh serial ports self.OnRefreshPortsClick(None) self.m_menuItemDisconnect.Enable(False) # Load settings from file when available self.m_settings_file = settings_file self.m_panel_changed = False if self.m_settings_file and os.path.exists(self.m_settings_file): self.load_settings(self.m_settings_file) else: # Create default relay panel self.new_relay_panel() # Disable controls on relay panel self.disable_relay_panels() # Disable relay menu self.disable_relay_menu() self.m_notebook.Bind(wx.EVT_CONTEXT_MENU, self.OnContext)
def __init__(self, parent, serial_port=None): frmModbus.__init__(self, parent) # Set default serial port self._serial_port = serial_port # Set application icon ico_path = resource_path('images/modbus.ico') if os.path.exists(ico_path): self.SetIcon(wx.Icon(ico_path)) # Initialize control states self.controls_disconnect() # Create MODBUS object self._modbus = relay_modbus.Modbus() # Transmit / receive frame counter self.frame_count = 0 # Create MODBUS receive thread self._modbus_receive_thread = ModbusReceiveThread( self._modbus, self.append_log) self._modbus_receive_thread.daemon = True self._modbus_receive_thread.start() # Load serial ports self.OnBtnRefreshClick(None) if self._serial_port: # Open default serial port self.OnBtnConnectDisconnect() self.m_cmbCommand.SetFocus() else: # Set default status bar text self.m_statusBar.SetStatusText( 'Select a serial port and click Open')
def CreateObjects(self, serial_port, address): # Check argument types assert type(serial_port) == str assert type(address) == int # Create MODBUS object and open serial port self.m_relay_modbus = relay_modbus.Modbus(serial_port) try: self.m_relay_modbus.open() except relay_modbus.SerialOpenException as err: wx.MessageBox(str(err), u'Failure', style=wx.OK | wx.ICON_STOP) sys.exit(1) # Create relay board object self.m_relay_board = relay_boards.R421A08(self.m_relay_modbus) if address < 0 or address >= self.m_relay_board.num_addresses: wx.MessageBox(u'Invalid address {}'.format(address), u'Failure', style=wx.OK | wx.ICON_STOP) sys.exit(1) self.m_relay_board.address = address # Set window title self.SetTitle(u'{} address {}'.format(self.m_relay_modbus.serial_port, self.m_relay_board.address))
def argument_parser(args): """ Argument parser :param args: Commandline arguments :return: None """ # ---------------------------------------------- # Description and help strings description = \ 'Python script to control a 8 Channel RS485 MODBUS RTU relay board type R421A08.' help_serial_port = \ 'Serial port (such as COM1 or /dev/ttyUSB0)' help_address = \ 'Address of the board [0..{}] (Set DIP switches)'.format(R421A08_NUM_ADDRESSES - 1) help_relays = \ 'Relay numbers [1..{}] or * for all relays'.format(R421A08_NUM_RELAYS) # ---------------------------------------------------------------------------------------------- # Create argument parser _parser = argparse.ArgumentParser(description=description) # Serial port argument is always required _parser.add_argument('serial_port', metavar='<SERIAL_PORT>', type=str, help=help_serial_port) # Address argument is always required _parser.add_argument('address', metavar='<ADDRESS>', type=arg_check_address, help=help_address) # ---------------------------------------------------------------------------------------------- # Create sub command arguments _subparsers = _parser.add_subparsers(help='Relay command') # Create status argument _parser_status = _subparsers.add_parser('status', help='Read status') _parser_status.add_argument('relays', metavar='<RELAYS>', nargs='*', default=['*'], type=arg_check_relay, help=help_relays) _parser_status.add_argument('-v', '--verbose', action='store_true', help='Print verbose') _parser_status.set_defaults(func=relay_cmd_status) # Create poll argument # _parser_poll = _subparsers.add_parser('poll', help='Poll status') # _parser_poll.add_argument('relays', metavar='<RELAYS>', nargs='*', default=['*'], # type=arg_check_relay, help=help_relays) # _parser_poll.add_argument('-i', '--interval', type=int, help='Poll interval in seconds') # _parser_poll.add_argument('-v', '--verbose', action='store_true', help='Print verbose') # _parser_poll.set_defaults(func=relay_cmd_poll) # Create on argument _parser_on = _subparsers.add_parser('on', help='On') _parser_on.add_argument('relays', metavar='<RELAYS>', nargs='*', type=arg_check_relay, help=help_relays) _parser_on.add_argument('-v', '--verbose', action='store_true', help='Print verbose') _parser_on.set_defaults(func=relay_cmd_on) # Create off argument _parser_off = _subparsers.add_parser('off', help='Off') _parser_off.add_argument('relays', metavar='<RELAYS>', nargs='*', type=arg_check_relay, help=help_relays) _parser_off.add_argument('-v', '--verbose', action='store_true', help='Print verbose') _parser_off.set_defaults(func=relay_cmd_off) # Create toggle argument _parser_toggle = _subparsers.add_parser('toggle', help='Toggle') _parser_toggle.add_argument('relays', metavar='<RELAYS>', nargs='*', type=arg_check_relay, help=help_relays) _parser_toggle.add_argument('-v', '--verbose', action='store_true', help='Print verbose') _parser_toggle.set_defaults(func=relay_cmd_toggle) # Create latch argument _parser_latch = _subparsers.add_parser('latch', help='Latch') _parser_latch.add_argument('relays', metavar='<RELAYS>', nargs='*', type=arg_check_relay, help=help_relays) _parser_latch.add_argument('-v', '--verbose', action='store_true', help='Print verbose') _parser_latch.set_defaults(func=relay_cmd_latch) # Create momentary argument _parser_latch = _subparsers.add_parser('momentary', help='Momentary') _parser_latch.add_argument('relays', metavar='<RELAYS>', nargs='*', type=arg_check_relay, help=help_relays) _parser_latch.add_argument('-v', '--verbose', action='store_true', help='Print verbose') _parser_latch.set_defaults(func=relay_cmd_momentary) # Create delay argument _parser_moment = _subparsers.add_parser('delay', help='Delay') _parser_moment.add_argument('relays', metavar='<RELAYS>', nargs='*', type=arg_check_relay, help=help_relays) _parser_moment.add_argument( '-d', '--delay', type=arg_check_delay, default=2, help='Delay (1..255 seconds) Default: 2 seconds') _parser_moment.add_argument('-v', '--verbose', action='store_true', help='Print verbose') _parser_moment.set_defaults(func=relay_cmd_delay) # ---------------------------------------------------------------------------------------------- # Parse arguments _args = None try: _args = _parser.parse_args(args) # Check required arguments for argument in ['serial_port', 'address', 'relays', 'verbose']: if argument not in _args: raise AttributeError() except AttributeError: _parser.print_help() sys.exit(0) # Create relay_modbus object _modbus = relay_modbus.Modbus(_args.serial_port, verbose=_args.verbose) try: _modbus.open() except serial.SerialException: print_stderr('Error: Cannot open serial port: ' + args.serial_port) sys.exit(1) # Create relay board object _relay_board = relay_boards.R421A08(_modbus, address=_args.address, verbose=_args.verbose) _args.func(_args, relay_boards=_relay_board)
def test_crc_invalid_type(self): modbus_test = relay_modbus.Modbus(self._serial_port, verbose=False) modbus_test.open() self.assertRaises(AssertionError, modbus_test.crc, 0x00)
def test_modbus_check_properties(self): modbus_test = relay_modbus.Modbus(self._serial_port, verbose=False) modbus_test.open() self.assertEqual(modbus_test.serial_port, self._serial_port) self.assertEqual(modbus_test.baudrate, 9600)
def test_send_no_data(self): modbus_test = relay_modbus.Modbus(self._serial_port, verbose=False) modbus_test.open() self.assertRaises(AssertionError, modbus_test.send, None)
# Required: Configure serial port, for example: # On Windows: 'COMx' # On Linux: '/dev/ttyUSB0' SERIAL_PORT = 'COM3' def check(retval): if not retval: sys.exit(1) if __name__ == '__main__': print('Getting started R421A08 relay board\n') # Create MODBUS object _modbus = relay_modbus.Modbus(serial_port=SERIAL_PORT) # Open serial port try: _modbus.open() except relay_modbus.SerialOpenException as err: print(err) sys.exit(1) # Create relay board object board = relay_boards.R421A08(_modbus, address=1) print('Status all relays:') check(board.print_status_all()) time.sleep(1)
def test_crc_no_argument(self): modbus_test = relay_modbus.Modbus(self._serial_port, verbose=False) modbus_test.open() self.assertRaises(TypeError, modbus_test.crc)