Zeiterfassung und pyserial entfernt

zeit.py las eine Stempeluhr ueber die serielle Schnittstelle aus und schrieb
in die Tabelle zeiten. Kein Startskript rief es auf, kein Modul importierte
es. Damit faellt auch pyserial weg - ausser zwei Testdateien in sunspec2,
die nicht ausgefuehrt werden, hat serial keinen Nutzer mehr.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 20:55:26 +02:00
co-authored by Claude Opus 5
parent 79843aa2ae
commit 05ce194478
84 changed files with 0 additions and 17164 deletions
Binary file not shown.
@@ -1,13 +0,0 @@
**/__pycache__
*.pyc
*.pyo
documentation/_build
build
dist
*.egg-info
/MANIFEST
.idea
venv
xxx*
@@ -1,16 +0,0 @@
# Copyright Roger Meier <r.meier@siemens.com>
# SPDX-License-Identifier: BSD-3-Clause
language: python
python:
- 2.7
- 3.4
- 3.5
- 3.6
- pypy
- pypy3
script:
- python setup.py install
- python test/run_all_tests.py loop://
-825
View File
@@ -1,825 +0,0 @@
========================
pySerial Release Notes
========================
Version 1.0 13 Feb 2002
---------------------------
- First public release.
- Split from the pybsl application (see http://mspgcc.sourceforge.net)
New Features:
- Added Jython support
Version 1.1 14 Feb 2002
---------------------------
Bugfixes:
- Win32, when not specifying a timeout
- Typos in the Docs
New Features:
- added ``serialutil`` which provides a base class for the ``Serial``
objects.
- ``readline``, ``readlines``, ``writelines`` and ``flush`` are now supported
see README.txt for deatils.
Version 1.11 14 Feb 2002
---------------------------
Same as 1.1 but added missing files.
Version 1.12 18 Feb 2002
---------------------------
Removed unneeded constants to fix RH7.x problems.
Version 1.13 09 Apr 2002
---------------------------
Added alternate way for enabling rtscts (CNEW_RTSCTS is tried too)
If port opening fails, a ``SerialException`` is raised on all platforms
Version 1.14 29 May 2002
---------------------------
Added examples to archive
Added non-blocking mode for ``timeout=0`` (tnx Mat Martineau)
Bugfixes:
- win32 does now return the remaining characters on timeout
Version 1.15 04 Jun 2002
---------------------------
Bugfixes (win32):
- removed debug messages
- compatibility to win9x improved
Version 1.16 02 Jul 2002
---------------------------
Added implementation of RI and corrected RTS/CTS on Win32
Version 1.17 03 Jul 2002
---------------------------
Silly mix of two versions in win32 code corrected
Version 1.18 06 Dec 2002
---------------------------
Bugfixes (general):
- remove the mapping of flush to the destructive flushOutput as
this is not the expected behaviour.
- readline: EOL character for lines can be chosen idea by
John Florian.
Bugfixes (posix):
- cygwin port numbering fixed
- test each and every constant for it's existence in termios module,
use default if not existent (fix for Bug item #640214)
- wrong exception on nonexistent ports with /dev file. bug report
by Louis Cordier
Bugfixes (win32):
- RTS/CTS handling as suggested in Bug #635072
- bugfix of timeouts brought up by Markus Hoffrogge
Version 1.19 19 Mar 2003
---------------------------
Bugfixes (posix):
- removed ``dgux`` entry which actually had a wrong comment and is
probably not in use anywhere.
Bugfixes (win32):
- added ``int()`` conversion, [Bug 702120]
- remove code to set control lines in close method of win32
version. [Bug 669625]
Version 1.20 28 Aug 2003
---------------------------
- Added ``serial.device()`` for all platforms
Bugfixes (win32):
- don't recreate overlapped structures and events on each
read/write.
- don't set unneeded event masks.
- don't use DOS device names for ports > 9.
- remove send timeout (it's not used in the linux impl. anyway).
Version 1.21 30 Sep 2003
---------------------------
Bugfixes (win32):
- name for COM10 was not built correctly, found by Norm Davis.
Bugfixes (examples):
- small change in ``miniterm.py`` that should mage it run on cygwin,
[Bug 809904] submitted by Rolf Campbell.
Version 2.0b1 1 Oct 2003
---------------------------
Transition to the Python 2.0 series:
- New implementation only supports Python 2.2+, backwards compatibility
should be maintained almost everywhere.
The OS handles (like the ``hComPort`` or ``fd`` attribute) were prefixed
with an underscore. The different names stay, as anyone that uses one of
these has to write platform specific code anyway.
- Common base class ``serialutil.SerialBase`` for all implementations.
- ``PARITY_NONE``, ``PARITY_EVEN``, ``PARITY_ODD`` constants changed and all
these constants moved to ``serialutil.py`` (still available as
``serial.PARITY_NONE`` etc. and they should be used that way)
- Added ``serial.PARITY_NAMES`` (implemented in ``serialutil.PARITY_NAMES``).
This dictionary can be used to convert parity constants to meaningful
strings.
- Each Serial class and instance has a list of supported values:
``BAUDRATES``, ``BYTESIZES``, ``PARITIES``, ``STOPBITS``Ggg
(i.e. ``serial.Serial.BAUDRATES or s = serial.Serial; s.BAUDRATES``)
these values can be used to fill in value sin GUI dialogs etc.
- Creating a ``Serial()`` object without port spec returns an unconfigured,
closed port. Useful if a GUI dialog should take a port and configure
it.
- New methods for ``serial.Serial`` instances: ``open()``, ``isOpen()``
- A port can be opened and closed as many times as desired.
- Instances of ``serial.Serial`` have ``baudrate``, ``bytesize``, ``timeout``
etc. attributes implemented as properties, all can be set while the port is
opened. It will then be reconfigured.
- Improved ``__doc__``'s.
- New ``test_advanced.py`` for the property setting/getting testing.
- Small bugfix on posix with get* methods (return value should be true a
boolean).
- added a ``__repr__`` that returns a meaningful string will all the serial
setting, easy for debugging.
- The serialposix module does not throw an exception on unsupported
platforms, the message is still printed. The idea that it may still
work even if the platform itself s not known, it simply tries to do
the posix stuff anyway (It's likely that opening ports by number
fails, but by name it should work).
Version 2.0b2 4 Oct 2003
---------------------------
- Added serial port configuration dialog for wxPython to the examples.
- Added terminal application for wxPython with wxGlade design file
to the examples.
- Jython support is currently broken as Jython does not have a Python 2.2
compatible release out yet
Version 2.0 6 Nov 2003
---------------------------
- Fixes ``setup.py`` for older distutils
Version 2.1 28 Jul 2004
---------------------------
Bugfixes:
- Fix XON/XOFF values [Bug 975250]
Bugfixes (posix):
- ``fd == 0`` fix from Vsevolod Lobko
- netbsd fixes from Erik Lindgren
- Dynamically lookup baudrates and some cleanups
Bugfixes (examples):
- CRLF handling of ``miniterm.py`` should be more consistent on Win32
and others. Added LF only command line option
- Multithreading fixes to ``wxTerminal.py`` (helps with wxGTK)
- Small change for wxPython 2.5 in ``wxSerialConfigDialog.py`` [Bug 994856]
New Features:
- Implement write timeouts (``writeTimeout`` parameter)
Version 2.2 31 Jul 2005
---------------------------
Bugfixes:
- [Bug 1014227]: property <del> broken
- [Bug 1105687]: ``serial_tcp_example.py``: ``--localport`` option
- [Bug 1106313]: device (port) strings cannot be unicode
Bugfixes (posix):
- [Patch 1043436] Fix for [Bug 1043420] (OSError: EAGAIN)
- [Patch 1102700] ``fileno()`` added
- ensure disabled PARMRK
Bugfixes (win32):
- [Patch 983106]: keep RTS/CTS state on port setting changes
New Features:
- ``dsrdtr`` setting to enable/disable DSR/DTR flow control independently
from the ``rtscts`` setting. (Currently Win32 only, ignored on other
platforms)
Version 2.3 19 Jun 2008
---------------------------
New Features:
- iterator interface. ``for line in Serial(...): ...`` is now possible
Suggested by Bernhard Bender
- ``sendBreak()`` accepts a ``duration`` argument. Default duration increased.
- win32 handles \\.\COMx format automatically for com ports of higher number
(COM10 is internally translated to \\.\COM10 etc.)
- miniterm.py has a new feature to send a file (upload) and configurable
special characters for exit and upload. Refactored internals to class based
structure (upload and class refactoring by Colin D Bennett)
Bugfixes:
- [Bug 1451535] TCP/serial redirect example "--help"
- update VERSION variable
- update wxSerialConfigDialog.py and wxTerminal.py compatibility with
wxPython 2.8 (Peleg)
- Check for string in write function. Using unicode causes errors, this
helps catching errors early (Tom Lynn)
Bugfixes (posix):
- [Bug 1554183] setRTS/setDTR reference to non existing local "on"
- [Bug 1513653] file descriptor not closed when exception is thrown
- FreeBSD now uses cuadX instead of cuaaX (Patrick Phalen)
Bugfixes (win32):
- [Bug 1520357] Handle leak
- [Bug 1679013] Ignore exception raised by SetCommTimeout() in close().
- [Bug 1938118] process hang forever under XP
Version 2.4 6 Jul 2008
---------------------------
New Features:
- [Patch 1616790] pyserial: Add inter-character timeout feature
- [Patch 1924805] add a setBreak function
- Add mark/space parity
- Add .NET/Mono backend (IronPython)
Bugfixes (posix):
- [Bug 1783159] Arbitrary baud rates (Linux/Posix)
Bugfixes (win32):
- [Patch 1561423] Add mark/space parity, Win32
- [Bug 2000771] serial port CANNOT be specified by number on windows
- examples/scanwin32.py does no longer return \\.\ names
- fix \\.\ handling for some cases
Bugfixes (jython):
- The Jython backend tries javax.comm and gnu.io (Seo Sanghyeon)
Version 2.5-rc1 2009-07-30
---------------------------
New Features:
- Python 3.x support (through 2to3)
- compatible with Python io library (Python 2.6+)
- Support for Win32 is now written on the top of ctypes (bundled with
Python 2.5+) instead of pywin32 (patch by Giovanni Bajo).
- 1.5 stop bits (STOPBITS_ONE_POINT_FIVE, implemented on all platforms)
- miniterm application extended (CTRL+T -> menu)
- miniterm.py is now installed as "script"
- add scanlinux.py example
- add port_publisher example
- experimental RFC-2217 server support (examples/rfc2217_server.py)
- add ``getSettingsDict`` and ``applySettingsDict`` serial object methods
- use a ``poll`` based implementation on Posix, instead of a ``select`` based,
provides better error handling [removed again in later releases].
Bugfixes:
- Improve and fix tcp_serial_redirector example.
- [Bug 2603052] 5-bit mode (needs 1.5 stop bits in some cases)
Bugfixes (posix):
- [Bug 2810169] Propagate exceptions raised in serialposix _reconfigure
- [Bug 2562610] setting non standard baud rates on Darwin (Emmanuel Blot)
Bugfixes (win32):
- [Bug 2469098] parity PARITY_MARK, PARITY_SPACE isn't supported on win32
- [SF 2446218] outWaiting implemented
- [Bug 2392892] scanwin32.py better exception handling
- [Bug 2505422] scanwin32.py Vista 64bit compatibility
Version 2.5-rc2 2010-01-02
---------------------------
New Features:
- Documentation update, now written with Sphinx/ReST
- Updated miniterm.py example
- experimental RFC-2217 client support (serial.rfc2217.Serial, see docs)
- add ``loop://`` device for testing.
- add ``serial.serial_for_url`` factory function (support for native ports and
``rfc2217``, ``socket`` and ``loop`` URLs)
- add new example: ``rfc2217_server.py``
- tests live in their own directory now (no longer in examples)
Bugfixes:
- [Bug 2915810] Fix for suboption parsing in rfc2217
- Packaging bug (missed some files)
Bugfixes (posix):
- improve write timeout behavior
- [Bug 2836297] move Linux specific constants to not break other platforms
- ``poll`` based implementation for ``read`` is in a separate class
``PosixPollSerial``, as it is not supported well on all platforms (the
default ``Serial`` class uses select).
- changed error handling in ``read`` so that disconnected devices are
detected.
Bugfixes (win32):
- [Bug 2886763] hComPort doesn't get initialized for Serial(port=None)
Version 2.5 2010-07-22
---------------------------
New Features:
- [Bug 2976262] dsrdtr should default to False
``dsrdtr`` parameter default value changed from ``None`` (follow ``rtscts``
setting) to ``False``. This means ``rtscts=True`` enables hardware flow
control on RTS/CTS but no longer also on DTR/DSR. This change mostly
affects Win32 as on other platforms, that setting was ignored anyway.
- Improved xreadlines, it is now a generator function that yields lines as they
are received (previously it called readlines which would only return all
lines read after a read-timeout). However xreadlines is deprecated and not
available when the io module is used. Use ``for line in Serial(...):``
instead.
Bugfixes:
- [Bug 2925854] test.py produces exception with python 3.1
- [Bug 3029812] 2.5rc2 readline(s) doesn't work
Bugfixes (posix):
- [BUG 3006606] Nonblocking error - Unix platform
Bugfixes (win32):
- [Bug 2998169] Memory corruption at faster transmission speeds.
(bug introduced in 2.5-rc1)
Version 2.6 2011-11-02
---------------------------
New Features:
- Moved some of the examples to serial.tools so that they can be used
with ``python -m``
- serial port enumeration now included as ``serial.tools.list_ports``
- URL handlers for ``serial_for_url`` are now imported dynamically. This allows
to add protocols w/o editing files. The list
``serial.protocol_handler_packages`` can be used to add or remove user
packages with protocol handlers (see docs for details).
- new URL type: hwgrep://<regexp> uses list_ports module to search for ports
by their description
- several internal changes to improve Python 3.x compatibility (setup.py,
use of absolute imports and more)
Bugfixes:
- [Bug 3093882] calling open() on an already open port now raises an exception
- [Bug 3245627] connection-lost let rfc2217 hangs in closed loop
- [Patch 3147043] readlines() to support multi-character eol
Bugfixes (posix):
- [Patch 3316943] Avoid unneeded termios.tcsetattr calls in serialposix.py
- [Patch 2912349] Serial Scan as a Module with Mac Support
Bugfixes (win32):
- [Bug 3057499] writeTimeoutError when write Timeout is 0
- [Bug 3414327] Character out of range in list_ports_windows
- [Patch 3036175] Windows 98 Support fix
- [Patch 3054352] RTS automatic toggle, for RS485 functionality.
- Fix type definitions for 64 bit Windows compatibility
Version 2.7 2013-10-17
---------------------------
- Win32: setRTS and setDTR can be called before the port is opened and it will
set the initial state on port open.
- Posix: add platform specific method: outWaiting (already present for Win32)
- Posix: rename flowControl to setXON to match name on Win32, add
flowControlOut function
- rfc2217: zero polls value (baudrate, data size, stop bits, parity) (Erik
Lundh)
- Posix: [Patch pyserial:28] Accept any speed on Linux [update]
- Posix: [Patch pyserial:29] PosixSerial.read() should "ignore" errno.EINTR
- OSX: [Patch pyserial:27] Scan by VendorID/Product ID for USB Serial devices
- Ensure working with bytes in write() calls
Bugfixes:
- [Bug 3540332] SerialException not returned
- [Bug pyserial:145] Error in socket_connection.py
- [Bug pyserial:135] reading from socket with timeout=None causes TypeError
- [Bug pyserial:130] setup.py should not append py3k to package name
- [Bug pyserial:117] no error on lost conn w/socket://
Bugfixes (posix):
- [Patch 3462364] Fix: NameError: global name 'base' is not defined
- list_ports and device() for BSD updated (Anders Langworthy)
- [Bug 3518380] python3.2 -m serial.tools.list_ports error
- [Bug pyserial:137] Patch to add non-standard baudrates to Cygwin
- [Bug pyserial:141] open: Pass errno from IOError to SerialException
- [Bug pyserial:125] Undefined 'base' on list_ports_posix.py, function usb_lsusb
- [Bug pyserial:151] Serial.write() without a timeout uses 100% CPU on POSIX
- [Patch pyserial:30] [PATCH 1/1] serial.Serial() should not raise IOError.
Bugfixes (win32):
- [Bug 3444941] ctypes.WinError() unicode error
- [Bug 3550043] on Windows in tools global name 'GetLastError' is not defined
- [Bug pyserial:146] flush() does nothing in windows (despite docs)
- [Bug pyserial:144] com0com ports ignored due to missing "friendly name"
- [Bug pyserial:152] Cannot configure port, some setting was wrong. Can leave
port handle open but port not accessible
Version 3.0a0 2015-09-22
--------------------------
- Starting from this release, only Python 2.7 and 3.2 (or newer) are supported.
The source code is compatible to the 2.x and 3.x series without any changes.
The support for earlier Python versions than 2.7 is removed, please refer to
the pyserial-legacy (V2.x) series if older Python versions are a
requirement).
- Development moved to github, update links in docs.
- API changes: properties for ``rts``, ``dtr``, ``cts``, ``dsr``, ``cd``, ``ri``,
``in_waiting`` (instead of get/set functions)
- remove file ``FileLike`` class, add ``read_until`` and ``iread_until`` to
``SerialBase``
- RS485 support changed (``rts_toggle`` removed, added ``serial.rs485`` module
and ``rs485_mode`` property)
- ``socket://`` and ``rfc2217://`` handlers use the IPv6 compatible
``socket.create_connection``
- New URL handler: ``spy:://``.
- URL handlers now require the proper format (``?`` and ``&``) for arguments
instead of ``/`` (e.g. ``rfc2217://localhost:7000?ign_set_control&timeout=5.5``)
- Remove obsolete examples.
- Finish update to BSD license.
- Use setuptools if available, fall back to distutils if unavailable.
- miniterm: changed command line options
- miniterm: support encodings on serial port
- miniterm: new transformations, by default escape/convert all control characters
- list_ports: improved, added USB location (Linux, Win32)
- refactored code
- [FTR pyserial:37] Support fileno() function in the socket protocol
- Posix: [Patch pyserial:31] Mark/space parity on Linux
- Linux: [Patch pyserial:32] Module list_ports for linux should include the
product information as description.
- Java: fix 2 bugs (stop bits if/else and non-integer timeouts) (Torsten
Roemer)
- Update wxSerialConfigDialog.py to use serial.tools.list_ports.
- [Patch pyserial:34] Improvements to port_publisher.py example
- [Feature pyserial:39] Support BlueTooth serial port discovery on Linux
Bugfixes:
- [Bug pyserial:157] Implement inWaiting in protocol_socket
- [Bug pyserial:166] RFC2217 connections always fail
- [Bug pyserial:172] applySettingsDict() throws an error if the settings dictionary is not complete
- [Bug pyserial:185] SocketSerial.read() never returns data when timeout==0
Bugfixes (posix):
- [Bug pyserial:156] PosixSerial.open raises OSError rather than
SerialException when port open fails
- [Bug pyserial:163] serial.tools.list_ports.grep() fails if it encounters None type
- fix setXON
- [Patch pyserial:36 / 38] Make USB information work in python 3.4 and 2.7
- clear OCRNL/ONLCR flags (CR/LF translation settings)
- [Feature pyserial:38] RS485 Support
- [Bug pyserial:170] list_ports_posix not working properly for Cygwin
- [Bug pyserial:187] improve support for FreeBSD (list_ports_posix)
Bugfixes (win32):
- [Bug pyserial:169] missing "import time" in serialwin32.py
Bugfixes (cli):
- [Bug pyserial:159] write() in serialcli.py not working with IronPython 2.7.4
Version 3.0b1 2015-10-19
--------------------------
- list_ports: add ``vid``, ``pid``, ``serial_number``, ``product``,
``manufacturer`` and ``location`` attribute for USB devices.
- list_ports: update OSX implementation.
- list_ports: Raspberry Pi: internal port is found.
- serial_for_url: fix import (multiple packages in list)
- threaded: added new module implementing a reader thread
- tweak examples/wx*
- posix: add experimental implementation ``VTIMESerial``
- new URL handler ``alt://`` to select alternative implementations
Version 3.0 2015-12-28
------------------------
- minor fixes to setup.py (file list), inter_byte_timeout (not stored when
passed to __init__), rfc2217 (behavior of close when open failed),
list_ports (__str__), loop://, renamed ReaderThread
- hwgrep:// added options to pick n'th port, skip busy ports
- miniterm: --ask option added
Bugfixes (posix):
- [#26/#30] always call tcsettattr on open
- [#42] fix disregard read timeout if there is more data
- [#45] check for write timeout, even if EAGAIN was raised
Bugfixes (win32):
- [#27] fix race condition in ``read()``, fix minimal timeout issue
- race condition in nonblocking case
- [#49] change exception type in case SetCommState fails
- [#50] fixed issue with 0 timeout on windows 10
Version 3.0.1 2016-01-11
--------------------------
- special case for FDTIBUS in list_ports on win32 (#61)
Bugfixes:
- ``Serial`` keyword arguments, more on backward compatibility, fix #55
- list_ports: return name if product is None, fix for #54
- port_publisher: restore some sorting of ports
Version 3.1.0 2016-05-27
--------------------------
Improvements:
- improve error handling in ``alt://`` handler
- ``socket://`` internally used select, improves timeout behavior
- initial state of RTS/DTR: ignore error when setting on open posix
(support connecting to pty's)
- code style updates
- posix: remove "number_to_device" which is not called anymore
- add cancel_read and cancel_write to win32 and posix implementations
Bugfixes:
- [#68] aio: catch errors and close connection
- [#87] hexlify: update codec for Python 2
- [#100] setPort not implemented
- [#101] bug in serial.threaded.Packetizer with easy fix
- [#104] rfc2217 and socket: set timeout in create_connection
- [#107] miniterm.py fails to exit on failed serial port
Bugfixes (posix):
- [#59] fixes for RTS/DTR handling on open
- [#77] list_ports_osx: add missing import
- [#85] serialposix.py _set_rs485_mode() tries to read non-existing
rs485_settings.delay_rts_before_send
- [#96] patch: native RS485 is never enabled
Bugfixes (win32):
- fix bad super call and duplicate old-style __init__ call
- [#80] list_ports: Compatibility issue between Windows/Linux
Version 3.1.1 2016-06-12
--------------------------
Improvements:
- deprecate ``nonblocking()`` method on posix, the port is already in this
mode.
- style: use .format() in various places instead of "%" formatting
Bugfixes:
- [#122] fix bug in FramedPacket
- [#127] The Serial class in the .NET/Mono (IronPython) backend does not
implement the _reconfigure_port method
- [#123, #128] Avoid Python 3 syntax in aio module
Bugfixes (posix):
- [#126] PATCH: Check delay_before_tx/rx for None in serialposix.py
- posix: retry if interrupted in Serial.read
Bugfixes (win32):
- win32: handle errors of GetOverlappedResult in read(), fixes #121
Version 3.2.0 2016-10-14
--------------------------
See 3.2.1, this one missed a merge request related to removing aio.
Version 3.2.1 2016-10-14
--------------------------
Improvements:
- remove ``serial.aio`` in favor of separate package, ``pyserial-asyncio``
- add client mode to example ``tcp_serial_redirect.py``
- use of monotonic clock for timeouts, when available (Python 3.3 and up)
- [#169] arbitrary baud rate support for BSD family
- improve tests, improve ``loop://``
Bugfixes:
- [#137] Exception while cancel in miniterm (python3)
- [#143] Class Serial in protocol_loop.py references variable before assigning
to it
- [#149] Python 3 fix for threaded.FramedPacket
Bugfixes (posix):
- [#133] _update_dtr_state throws Inappropriate ioctl for virtual serial
port created by socat on OS X
- [#157] Broken handling of CMSPAR in serialposix.py
Bugfixes (win32):
- [#144] Use Unicode API for list_ports
- [#145] list_ports_windows: support devices with only VID
- [#162] Write in non-blocking mode returns incorrect value on windows
Version 3.3 2017-03-08
------------------------
Improvements:
- [#206] Exclusive access on POSIX. ``exclusive`` flag added.
- [#172] list_ports_windows: list_ports with 'manufacturer' info property
- [#174] miniterm: change cancel impl. for console
- [#182] serialutil: add overall timeout for read_until
- socket: use non-blocking socket and new Timeout class
- socket: implement a functional a reset_input_buffer
- rfc2217: improve read timeout implementation
- win32: include error message from system in ClearCommError exception
- and a few minor changes, docs
Bugfixes:
- [#183] rfc2217: Fix broken calls to to_bytes on Python3.
- [#188] rfc2217: fix auto-open use case when port is given as parameter
Bugfixes (posix):
- [#178] in read, count length of converted data
- [#189] fix return value of write
Bugfixes (win32):
- [#194] spurious write fails with ERROR_SUCCESS
Version 3.4 2017-07-22
------------------------
Improvements:
- miniterm: suspend function (temporarily release port, :kbd:`Ctrl-T s`)
- [#240] context manager automatically opens port on ``__enter__``
- [#141] list_ports: add interface number to location string
- [#225] protocol_socket: Retry if ``BlockingIOError`` occurs in
``reset_input_buffer``.
Bugfixes:
- [#153] list_ports: option to include symlinked devices
- [#237] list_ports: workaround for special characters in port names
Bugfixes (posix):
- allow calling cancel functions w/o error if port is closed
- [#220] protocol_socket: sync error handling with posix version
- [#227] posix: ignore more blocking errors and EINTR, timeout only
applies to blocking I/O
- [#228] fix: port_publisher typo
Version 3.5b0 2020-09-21
------------------------
New Features:
- [#411] Add a backend for Silicon Labs CP2110/4 HID-to-UART bridge.
(depends on `hid` module)
Improvements:
- [#315] Use absolute import everywhere
- [#351] win32: miniterm Working CMD.exe terminal using Windows 10 ANSI support
- [#354] Make ListPortInfo hashable
- [#372] threaded: "write" returns byte count
- [#400] Add bytesize and stopbits argument parser to tcp_serial_redirect
- [#408] loop: add out_waiting
- [#495] list_ports_linux: Correct "interface" property on Linux hosts
- [#500] Remove Python 3.2 and 3.3 from test
- [#261, #285, #296, #320, #333, #342, #356, #358, #389, #397, #510] doc updates
- miniterm: add :kbd:`CTRL+T Q` as alternative to exit
- miniterm: suspend function key changed to :kbd:`CTRL-T Z`
- add command line tool entries ``pyserial-miniterm`` (replaces ``miniterm.py``)
and ``pyserial-ports`` (runs ``serial.tools.list_ports``).
- ``python -m serial`` opens miniterm (use w/o args and it will print port
list too) [experimental]
Bugfixes:
- [#371] Don't open port if self.port is not set while entering context manager
- [#437, #502] refactor: raise new instances for PortNotOpenError and SerialTimeoutException
- [#261, #263] list_ports: set default `name` attribute
- [#286] fix: compare only of the same type in list_ports_common.ListPortInfo
- rfc2217/close(): fix race-condition
- [#305] return b'' when connection closes on rfc2217 connection
- [#386] rfc2217/close(): fix race condition
- Fixed flush_input_buffer() for situations where the remote end has closed the socket.
- [#441] reset_input_buffer() can hang on sockets
- examples: port_publisher python 3 fixes
- [#324] miniterm: Fix miniterm constructor exit_character and menu_character
- [#326] miniterm: use exclusive access for native serial ports by default
- [#497] miniterm: fix double use of CTRL-T + s use z for suspend instead
- [#443, #444] examples: refactor wx example, use Bind to avoid deprecated
warnings, IsChecked, unichr
Bugfixes (posix):
- [#265] posix: fix PosixPollSerial with timeout=None and add cancel support
- [#290] option for low latency mode on linux
- [#335] Add support to xr-usb-serial ports
- [#494] posix: Don't catch the SerialException we just raised
- [#519] posix: Fix custom baud rate to not temporarily set 38400 baud rates on linux
- [#509 #518] list_ports: use hardcoded path to library on osx
Bugfixes (win32):
- [#481] win32: extend RS485 error messages
- [#303] win32: do not check for links in serial.tools.list_ports
- [#430] Add WaitCommEvent function to win32
- [#314, #433] tools/list_ports_windows: Scan both 'Ports' and 'Modem' device classes
- [#414] Serial number support for composite USB devices
- Added recursive search for device USB serial number to support composite devices
Bugfixes (MacOS):
- [#364] MacOS: rework list_ports to support unicode product descriptors.
- [#367] Mac and bsd fix _update_break_state
Version 3.5 2020-11-23
----------------------
See above (3.5b0) for what's all new in this release
Bugfixes:
- spy: ensure bytes in write()
Bugfixes (posix):
- [#540] serialposix: Fix inconsistent state after exception in open()
Bugfixes (win32):
- [#530] win32: Fix exception for composite serial number search on Windows
Bugfixes (MacOS):
- [#542] list_ports_osx: kIOMasterPortDefault no longer exported on Big Sur
- [#545, #545] list_ports_osx: getting USB info on BigSur/AppleSilicon
@@ -1,39 +0,0 @@
Copyright (c) 2001-2020 Chris Liechti <cliechti@gmx.net>
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
Note:
Individual files contain the following tag instead of the full license text.
SPDX-License-Identifier: BSD-3-Clause
This enables machine processing of license information based on the SPDX
License Identifiers that are here available: http://spdx.org/licenses/
@@ -1,39 +0,0 @@
include README.rst
include LICENSE.txt
include CHANGES.rst
include MANIFEST.in
include setup.py
include setup.cfg
include pylintrc
include examples/at_protocol.py
include examples/port_publisher.py
include examples/port_publisher.sh
include examples/rfc2217_server.py
include examples/setup-miniterm-py2exe.py
include examples/setup-rfc2217_server-py2exe.py
include examples/setup-wxTerminal-py2exe.py
include examples/tcp_serial_redirect.py
include examples/wxSerialConfigDialog.py
include examples/wxSerialConfigDialog.wxg
include examples/wxTerminal.py
include examples/wxTerminal.wxg
include test/handlers/__init__.py
include test/handlers/protocol_test.py
include test/run_all_tests.py
include test/test_advanced.py
include test/test_high_load.py
include test/test_iolib.py
include test/test.py
include test/test_readline.py
include test/test_rfc2217.py
include test/test_rs485.py
include test/test_settings_dict.py
include test/test_url.py
include documentation/*.rst
include documentation/pyserial.png
include documentation/conf.py
include documentation/Makefile
@@ -1,56 +0,0 @@
=================================
pySerial |build-status| |docs|
=================================
Overview
========
This module encapsulates the access for the serial port. It provides backends
for Python_ running on Windows, OSX, Linux, BSD (possibly any POSIX compliant
system) and IronPython. The module named "serial" automatically selects the
appropriate backend.
- Project Homepage: https://github.com/pyserial/pyserial
- Download Page: https://pypi.python.org/pypi/pyserial
BSD license, (C) 2001-2020 Chris Liechti <cliechti@gmx.net>
Documentation
=============
For API documentation, usage and examples see files in the "documentation"
directory. The ".rst" files can be read in any text editor or being converted to
HTML or PDF using Sphinx_. An HTML version is online at
https://pythonhosted.org/pyserial/
Examples
========
Examples and unit tests are in the directory examples_.
Installation
============
``pip install pyserial`` should work for most users.
Detailed information can be found in `documentation/pyserial.rst`_.
The usual setup.py for Python_ libraries is used for the source distribution.
Windows installers are also available (see download link above).
or
To install this package with conda run:
``conda install -c conda-forge pyserial``
conda builds are available for linux, mac and windows.
.. _`documentation/pyserial.rst`: https://github.com/pyserial/pyserial/blob/master/documentation/pyserial.rst#installation
.. _examples: https://github.com/pyserial/pyserial/blob/master/examples
.. _Python: http://python.org/
.. _Sphinx: http://sphinx-doc.org/
.. |build-status| image:: https://travis-ci.org/pyserial/pyserial.svg?branch=master
:target: https://travis-ci.org/pyserial/pyserial
:alt: Build status
.. |docs| image:: https://readthedocs.org/projects/pyserial/badge/?version=latest
:target: http://pyserial.readthedocs.io/
:alt: Documentation
@@ -1,88 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf _build/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
@echo
@echo "Build finished. The HTML pages are in _build/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml
@echo
@echo "Build finished. The HTML pages are in _build/dirhtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in _build/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in _build/qthelp, like this:"
@echo "# qcollectiongenerator _build/qthelp/pySerial.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile _build/qthelp/pySerial.qhc"
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
@echo
@echo "Build finished; the LaTeX files are in _build/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes
@echo
@echo "The overview file is in _build/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in _build/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in _build/doctest/output.txt."
@@ -1,148 +0,0 @@
==========
Appendix
==========
How To
======
Enable :rfc:`2217` (and other URL handlers) in programs using pySerial.
Patch the code where the :class:`serial.Serial` is instantiated.
E.g. replace::
s = serial.Serial(...)
it with::
s = serial.serial_for_url(...)
or for backwards compatibility to old pySerial installations::
try:
s = serial.serial_for_url(...)
except AttributeError:
s = serial.Serial(...)
Assuming the application already stores port names as strings that's all
that is required. The user just needs a way to change the port setting of
your application to an ``rfc2217://`` :ref:`URL <URLs>` (e.g. by editing a
configuration file, GUI dialog etc.).
Please note that this enables all :ref:`URL <URLs>` types supported by
pySerial and that those involving the network are unencrypted and not
protected against eavesdropping.
Test your setup.
Is the device not working as expected? Maybe it's time to check the
connection before proceeding. :ref:`miniterm` from the :ref:`examples`
can be used to open the serial port and do some basic tests.
To test cables, connecting RX to TX (loop back) and typing some characters
in :ref:`miniterm` is a simple test. When the characters are displayed
on the screen, then at least RX and TX work (they still could be swapped
though).
There is also a ``spy:://`` URL handler. It prints all calls (read/write,
control lines) to the serial port to a file or stderr. See :ref:`spy`
for details.
FAQ
===
Example works in :ref:`miniterm` but not in script.
The RTS and DTR lines are switched when the port is opened. This may cause
some processing or reset on the connected device. In such a cases an
immediately following call to :meth:`write` may not be received by the
device.
A delay after opening the port, before the first :meth:`write`, is
recommended in this situation. E.g. a ``time.sleep(1)``
Application works when .py file is run, but fails when packaged (py2exe etc.)
py2exe and similar packaging programs scan the sources for import
statements and create a list of modules that they package. pySerial may
create two issues with that:
- implementations for other modules are found. On Windows, it's safe to
exclude 'serialposix', 'serialjava' and 'serialcli' as these are not
used.
- :func:`serial.serial_for_url` does a dynamic lookup of protocol handlers
at runtime. If this function is used, the desired handlers have to be
included manually (e.g. 'serial.urlhandler.protocol_socket',
'serial.urlhandler.protocol_rfc2217', etc.). This can be done either with
the "includes" option in ``setup.py`` or by a dummy import in one of the
packaged modules.
User supplied URL handlers
:func:`serial.serial_for_url` can be used to access "virtual" serial ports
identified by an :ref:`URL <URLs>` scheme. E.g. for the :rfc:`2217`:
``rfc2217://``.
Custom :ref:`URL <URLs>` handlers can be added by extending the module
search path in :data:`serial.protocol_handler_packages`. This is possible
starting from pySerial V2.6.
``Permission denied`` errors
On POSIX based systems, the user usually needs to be in a special group to
have access to serial ports.
On Debian based systems, serial ports are usually in the group ``dialout``,
so running ``sudo adduser $USER dialout`` (and logging-out and -in) enables
the user to access the port.
Parity on Raspberry Pi
The Raspi has one full UART and a restricted one. On devices with built
in wireless (WIFI/BT) use the restricted one on the GPIO header pins.
If enhanced features are required, it is possible to swap UARTs, see
https://www.raspberrypi.org/documentation/configuration/uart.md
Support for Python 2.6 or earlier
Support for older Python releases than 2.7 will not return to pySerial 3.x.
Python 2.7 is now many years old (released 2010). If you insist on using
Python 2.6 or earlier, it is recommend to use pySerial `2.7`_
(or any 2.x version).
.. _`2.7`: https://pypi.python.org/pypi/pyserial/2.7
Related software
================
com0com - http://com0com.sourceforge.net/
Provides virtual serial ports for Windows.
License
=======
Copyright (c) 2001-2020 Chris Liechti <cliechti@gmx.net>
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -1,200 +0,0 @@
# -*- coding: utf-8 -*-
#
# pySerial documentation build configuration file, created by
# sphinx-quickstart on Tue Jul 21 00:27:45 2009.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.append(os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'pySerial'
copyright = u'2001-2020, Chris Liechti'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '3.4'
# The full version, including alpha/beta/rc tags.
release = '3.4'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
#html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
html_logo = 'pyserial.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
#htmlhelp_basename = 'pySerialdoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'pySerial.tex', u'pySerial Documentation',
u'Chris Liechti', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
latex_logo = 'pyserial.png'
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
# for external links to standard library
intersphinx_mapping = {
#~ 'python': ('http://docs.python.org', None),
'py': ('http://docs.python.org', None),
}
@@ -1,274 +0,0 @@
.. _examples:
==========
Examples
==========
Miniterm
========
Miniterm is now available as module instead of example.
see :ref:`miniterm` for details.
miniterm.py_
The miniterm program.
setup-miniterm-py2exe.py_
This is a py2exe setup script for Windows. It can be used to create a
standalone ``miniterm.exe``.
.. _miniterm.py: https://github.com/pyserial/pyserial/blob/master/serial/tools/miniterm.py
.. _setup-miniterm-py2exe.py: https://github.com/pyserial/pyserial/blob/master/examples/setup-miniterm-py2exe.py
TCP/IP - serial bridge
======================
This program opens a TCP/IP port. When a connection is made to that port (e.g.
with telnet) it forwards all data to the serial port and vice versa.
This example only exports a raw socket connection. The next example
below gives the client much more control over the remote serial port.
- The serial port settings are set on the command line when starting the
program.
- There is no possibility to change settings from remote.
- All data is passed through as-is.
::
usage: tcp_serial_redirect.py [-h] [-q] [--parity {N,E,O,S,M}] [--rtscts]
[--xonxoff] [--rts RTS] [--dtr DTR]
[-P LOCALPORT]
SERIALPORT [BAUDRATE]
Simple Serial to Network (TCP/IP) redirector.
positional arguments:
SERIALPORT serial port name
BAUDRATE set baud rate, default: 9600
optional arguments:
-h, --help show this help message and exit
-q, --quiet suppress non error messages
serial port:
--parity {N,E,O,S,M} set parity, one of {N E O S M}, default: N
--rtscts enable RTS/CTS flow control (default off)
--xonxoff enable software flow control (default off)
--rts RTS set initial RTS line state (possible values: 0, 1)
--dtr DTR set initial DTR line state (possible values: 0, 1)
network settings:
-P LOCALPORT, --localport LOCALPORT
local TCP port
NOTE: no security measures are implemented. Anyone can remotely connect to
this service over the network. Only one connection at once is supported. When
the connection is terminated it waits for the next connect.
tcp_serial_redirect.py_
Main program.
.. _tcp_serial_redirect.py: https://github.com/pyserial/pyserial/blob/master/examples/tcp_serial_redirect.py
Single-port TCP/IP - serial bridge (RFC 2217)
=============================================
Simple cross platform :rfc:`2217` serial port server. It uses threads and is
portable (runs on POSIX, Windows, etc).
- The port settings and control lines (RTS/DTR) can be changed at any time
using :rfc:`2217` requests. The status lines (DSR/CTS/RI/CD) are polled every
second and notifications are sent to the client.
- Telnet character IAC (0xff) needs to be doubled in data stream. IAC followed
by another value is interpreted as Telnet command sequence.
- Telnet negotiation commands are sent when connecting to the server.
- RTS/DTR are activated on client connect and deactivated on disconnect.
- Default port settings are set again when client disconnects.
::
usage: rfc2217_server.py [-h] [-p TCPPORT] [-v] SERIALPORT
RFC 2217 Serial to Network (TCP/IP) redirector.
positional arguments:
SERIALPORT
optional arguments:
-h, --help show this help message and exit
-p TCPPORT, --localport TCPPORT
local TCP port, default: 2217
-v, --verbose print more diagnostic messages (option can be given
multiple times)
NOTE: no security measures are implemented. Anyone can remotely connect to
this service over the network. Only one connection at once is supported. When
the connection is terminated it waits for the next connect.
.. versionadded:: 2.5
rfc2217_server.py_
Main program.
setup-rfc2217_server-py2exe.py_
This is a py2exe setup script for Windows. It can be used to create a
standalone ``rfc2217_server.exe``.
.. _rfc2217_server.py: https://github.com/pyserial/pyserial/blob/master/examples/rfc2217_server.py
.. _setup-rfc2217_server-py2exe.py: https://github.com/pyserial/pyserial/blob/master/examples/setup-rfc2217_server-py2exe.py
Multi-port TCP/IP - serial bridge (RFC 2217)
============================================
This example implements a TCP/IP to serial port service that works with
multiple ports at once. It uses select, no threads, for the serial ports and
the network sockets and therefore runs on POSIX systems only.
- Full control over the serial port with :rfc:`2217`.
- Check existence of ``/tty/USB0...8``. This is done every 5 seconds using
``os.path.exists``.
- Send zeroconf announcements when port appears or disappears (uses
python-avahi and dbus). Service name: ``_serial_port._tcp``.
- Each serial port becomes available as one TCP/IP server. e.g.
``/dev/ttyUSB0`` is reachable at ``<host>:7000``.
- Single process for all ports and sockets (not per port).
- The script can be started as daemon.
- Logging to stdout or when run as daemon to syslog.
- Default port settings are set again when client disconnects.
- modem status lines (CTS/DSR/RI/CD) are not polled periodically and the server
therefore does not send NOTIFY_MODEMSTATE on its own. However it responds to
request from the client (i.e. use the ``poll_modem`` option in the URL when
using a pySerial client.)
::
usage: port_publisher.py [options]
Announce the existence of devices using zeroconf and provide
a TCP/IP <-> serial port gateway (implements RFC 2217).
If running as daemon, write to syslog. Otherwise write to stdout.
optional arguments:
-h, --help show this help message and exit
serial port settings:
--ports-regex REGEX specify a regex to search against the serial devices
and their descriptions (default: /dev/ttyUSB[0-9]+)
network settings:
--tcp-port PORT specify lowest TCP port number (default: 7000)
daemon:
-d, --daemon start as daemon
--pidfile FILE specify a name for the PID file
diagnostics:
-o FILE, --logfile FILE
write messages file instead of stdout
-q, --quiet suppress most diagnostic messages
-v, --verbose increase diagnostic messages
NOTE: no security measures are implemented. Anyone can remotely connect to
this service over the network. Only one connection at once, per port, is
supported. When the connection is terminated, it waits for the next connect.
Requirements:
- Python (>= 2.4)
- python-avahi
- python-dbus
- python-serial (>= 2.5)
Installation as daemon:
- Copy the script ``port_publisher.py`` to ``/usr/local/bin``.
- Copy the script ``port_publisher.sh`` to ``/etc/init.d``.
- Add links to the runlevels using ``update-rc.d port_publisher.sh defaults 99``
- That's it :-) the service will be started on next reboot. Alternatively run
``invoke-rc.d port_publisher.sh start`` as root.
.. versionadded:: 2.5 new example
port_publisher.py_
Multi-port TCP/IP-serial converter (RFC 2217) for POSIX environments.
port_publisher.sh_
Example init.d script.
.. _port_publisher.py: https://github.com/pyserial/pyserial/blob/master/examples/port_publisher.py
.. _port_publisher.sh: https://github.com/pyserial/pyserial/blob/master/examples/port_publisher.sh
wxPython examples
=================
A simple terminal application for wxPython and a flexible serial port
configuration dialog are shown here.
wxTerminal.py_
A simple terminal application. Note that the length of the buffer is
limited by wx and it may suddenly stop displaying new input.
wxTerminal.wxg_
A wxGlade design file for the terminal application.
wxSerialConfigDialog.py_
A flexible serial port configuration dialog.
wxSerialConfigDialog.wxg_
The wxGlade design file for the configuration dialog.
setup-wxTerminal-py2exe.py_
A py2exe setup script to package the terminal application.
.. _wxTerminal.py: https://github.com/pyserial/pyserial/blob/master/examples/wxTerminal.py
.. _wxTerminal.wxg: https://github.com/pyserial/pyserial/blob/master/examples/wxTerminal.wxg
.. _wxSerialConfigDialog.py: https://github.com/pyserial/pyserial/blob/master/examples/wxSerialConfigDialog.py
.. _wxSerialConfigDialog.wxg: https://github.com/pyserial/pyserial/blob/master/examples/wxSerialConfigDialog.wxg
.. _setup-wxTerminal-py2exe.py: https://github.com/pyserial/pyserial/blob/master/examples/setup-wxTerminal-py2exe.py
Unit tests
==========
The project uses a number of unit test to verify the functionality. They all
need a loop back connector. The scripts itself contain more information. All
test scripts are contained in the directory ``test``.
The unit tests are performed on port ``loop://`` unless a different device
name or URL is given on the command line (``sys.argv[1]``). e.g. to run the
test on an attached USB-serial converter ``hwgrep://USB`` could be used or
the actual name such as ``/dev/ttyUSB0`` or ``COM1`` (depending on platform).
run_all_tests.py_
Collect all tests from all ``test*`` files and run them. By default, the
``loop://`` device is used.
test.py_
Basic tests (binary capabilities, timeout, control lines).
test_advanced.py_
Test more advanced features (properties).
test_high_load.py_
Tests involving sending a lot of data.
test_readline.py_
Tests involving ``readline``.
test_iolib.py_
Tests involving the :mod:`io` library. Only available for Python 2.6 and
newer.
test_url.py_
Tests involving the :ref:`URL <URLs>` feature.
.. _run_all_tests.py: https://github.com/pyserial/pyserial/blob/master/test/run_all_tests.py
.. _test.py: https://github.com/pyserial/pyserial/blob/master/test/test.py
.. _test_advanced.py: https://github.com/pyserial/pyserial/blob/master/test/test_advanced.py
.. _test_high_load.py: https://github.com/pyserial/pyserial/blob/master/test/test_high_load.py
.. _test_readline.py: https://github.com/pyserial/pyserial/blob/master/test/test_readline.py
.. _test_iolib.py: https://github.com/pyserial/pyserial/blob/master/test/test_iolib.py
.. _test_url.py: https://github.com/pyserial/pyserial/blob/master/test/test_url.py
@@ -1,44 +0,0 @@
.. pySerial documentation master file
.. _welcome:
Welcome to pySerial's documentation
===================================
This module encapsulates the access for the serial port. It provides backends
for Python_ running on Windows, OSX, Linux, BSD (possibly any POSIX compliant
system) and IronPython. The module named "serial" automatically selects the
appropriate backend.
Other pages (online)
- `project page on GitHub`_
- `Download Page`_ with releases
- This page, when viewed online is at https://pyserial.readthedocs.io/en/latest/ or
http://pythonhosted.org/pyserial/ .
.. _Python: http://python.org/
.. _`project page on GitHub`: https://github.com/pyserial/
.. _`Download Page`: http://pypi.python.org/pypi/pyserial
Contents:
.. toctree::
:maxdepth: 2
pyserial
shortintro
pyserial_api
tools
url_handlers
examples
appendix
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

@@ -1,145 +0,0 @@
==========
pySerial
==========
Overview
========
This module encapsulates the access for the serial port. It provides backends
for Python_ running on Windows, OSX, Linux, BSD (possibly any POSIX compliant
system) and IronPython. The module named "serial" automatically selects the
appropriate backend.
It is released under a free software license, see LICENSE_ for more
details.
Copyright (C) 2001-2020 Chris Liechti <cliechti(at)gmx.net>
Other pages (online)
- `project page on GitHub`_
- `Download Page`_ with releases (PyPi)
- This page, when viewed online is at https://pyserial.readthedocs.io/en/latest/ or
http://pythonhosted.org/pyserial/ .
.. _Python: http://python.org/
.. _LICENSE: appendix.html#license
.. _`project page on GitHub`: https://github.com/pyserial/pyserial/
.. _`Download Page`: http://pypi.python.org/pypi/pyserial
Features
========
- Same class based interface on all supported platforms.
- Access to the port settings through Python properties.
- Support for different byte sizes, stop bits, parity and flow control with
RTS/CTS and/or Xon/Xoff.
- Working with or without receive timeout.
- File like API with "read" and "write" ("readline" etc. also supported).
- The files in this package are 100% pure Python.
- The port is set up for binary transmission. No NULL byte stripping, CR-LF
translation etc. (which are many times enabled for POSIX.) This makes this
module universally useful.
- Compatible with :mod:`io` library
- RFC 2217 client (experimental), server provided in the examples.
Requirements
============
- Python 2.7 or Python 3.4 and newer
- If running on Windows: Windows 7 or newer
- If running on Jython: "Java Communications" (JavaComm) or compatible
extension for Java
For older installations (older Python versions or older operating systems), see
`older versions`_ below.
Installation
============
This installs a package that can be used from Python (``import serial``).
To install for all users on the system, administrator rights (root)
may be required.
From PyPI
---------
pySerial can be installed from PyPI::
python -m pip install pyserial
Using the `python`/`python3` executable of the desired version (2.7/3.x).
Developers also may be interested to get the source archive, because it
contains examples, tests and the this documentation.
From Conda
----------
pySerial can be installed from Conda::
conda install pyserial
or
conda install -c conda-forge pyserial
Currently the default conda channel will provide version 3.4 whereas the
conda-forge channel provides the current 3.x version.
Conda: https://www.continuum.io/downloads
From source (zip/tar.gz or checkout)
------------------------------------
Download the archive from http://pypi.python.org/pypi/pyserial or
https://github.com/pyserial/pyserial/releases.
Unpack the archive, enter the ``pyserial-x.y`` directory and run::
python setup.py install
Using the `python`/`python3` executable of the desired version (2.7/3.x).
Packages
--------
There are also packaged versions for some Linux distributions:
- Debian/Ubuntu: "python-serial", "python3-serial"
- Fedora / RHEL / CentOS / EPEL: "pyserial"
- Arch Linux: "python-pyserial"
- Gentoo: "dev-python/pyserial"
Note that some distributions may package an older version of pySerial.
These packages are created and maintained by developers working on
these distributions.
.. _PyPi: http://pypi.python.org/pypi/pyserial
References
==========
* Python: http://www.python.org/
* Jython: http://www.jython.org/
* IronPython: http://www.codeplex.com/IronPython
Older Versions
==============
Older versions are still available on the current download_ page or the `old
download`_ page. The last version of pySerial's 2.x series was `2.7`_,
compatible with Python 2.3 and newer and partially with early Python 3.x
versions.
pySerial `1.21`_ is compatible with Python 2.0 on Windows, Linux and several
un*x like systems, MacOSX and Jython.
On Windows, releases older than 2.5 will depend on pywin32_ (previously known as
win32all). WinXP is supported up to 3.0.1.
.. _`old download`: https://sourceforge.net/projects/pyserial/files/pyserial/
.. _download: https://pypi.python.org/simple/pyserial/
.. _pywin32: http://pypi.python.org/pypi/pywin32
.. _`2.7`: https://pypi.python.org/pypi/pyserial/2.7
.. _`1.21`: https://sourceforge.net/projects/pyserial/files/pyserial/1.21/pyserial-1.21.zip/download
File diff suppressed because it is too large Load Diff
@@ -1,123 +0,0 @@
====================
Short introduction
====================
Opening serial ports
====================
Open port at "9600,8,N,1", no timeout::
>>> import serial
>>> ser = serial.Serial('/dev/ttyUSB0') # open serial port
>>> print(ser.name) # check which port was really used
>>> ser.write(b'hello') # write a string
>>> ser.close() # close port
Open named port at "19200,8,N,1", 1s timeout::
>>> with serial.Serial('/dev/ttyS1', 19200, timeout=1) as ser:
... x = ser.read() # read one byte
... s = ser.read(10) # read up to ten bytes (timeout)
... line = ser.readline() # read a '\n' terminated line
Open port at "38400,8,E,1", non blocking HW handshaking::
>>> ser = serial.Serial('COM3', 38400, timeout=0,
... parity=serial.PARITY_EVEN, rtscts=1)
>>> s = ser.read(100) # read up to one hundred bytes
... # or as much is in the buffer
Configuring ports later
=======================
Get a Serial instance and configure/open it later::
>>> ser = serial.Serial()
>>> ser.baudrate = 19200
>>> ser.port = 'COM1'
>>> ser
Serial<id=0xa81c10, open=False>(port='COM1', baudrate=19200, bytesize=8, parity='N', stopbits=1, timeout=None, xonxoff=0, rtscts=0)
>>> ser.open()
>>> ser.is_open
True
>>> ser.close()
>>> ser.is_open
False
Also supported with :ref:`context manager <context-manager>`::
with serial.Serial() as ser:
ser.baudrate = 19200
ser.port = 'COM1'
ser.open()
ser.write(b'hello')
.. _shortintro_readline:
Readline
========
:meth:`readline` reads up to one line, including the ``\n`` at the end.
Be careful when using :meth:`readline`. Do specify a timeout when opening the
serial port otherwise it could block forever if no newline character is
received. If the ``\n`` is missing in the return value, it returned on timeout.
:meth:`readlines` tries to read "all" lines which is not well defined for a
serial port that is still open. Therefore :meth:`readlines` depends on having
a timeout on the port and interprets that as EOF (end of file). It raises an
exception if the port is not opened correctly. The returned list of lines do
not include the ``\n``.
Both functions call :meth:`read` to get their data and the serial port timeout
is acting on this function. Therefore the effective timeout, especially for
:meth:`readlines`, can be much larger.
Do also have a look at the example files in the examples directory in the
source distribution or online.
.. note::
The ``eol`` parameter for :meth:`readline` is no longer supported when
pySerial is run with newer Python versions (V2.6+) where the module
:mod:`io` is available.
EOL
---
To specify the EOL character for :meth:`readline` or to use universal newline
mode, it is advised to use io.TextIOWrapper_::
import serial
import io
ser = serial.serial_for_url('loop://', timeout=1)
sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser))
sio.write(unicode("hello\n"))
sio.flush() # it is buffering. required to get the data out *now*
hello = sio.readline()
print(hello == unicode("hello\n"))
.. _io.TextIOWrapper: http://docs.python.org/library/io.html#io.TextIOWrapper
Testing ports
=============
Listing ports
-------------
``python -m serial.tools.list_ports`` will print a list of available ports. It
is also possible to add a regexp as first argument and the list will only
include entries that matched.
.. note::
The enumeration may not work on all operating systems. It may be
incomplete, list unavailable ports or may lack detailed descriptions of the
ports.
.. versionadded: 2.6
Accessing ports
---------------
pySerial includes a small console based terminal program called
:ref:`miniterm`. It can be started with ``python -m serial.tools.miniterm <port_name>``
(use option ``-h`` to get a listing of all options).
@@ -1,291 +0,0 @@
=======
Tools
=======
.. module:: serial
serial.tools.list_ports
=======================
.. module:: serial.tools.list_ports
This module can be executed to get a list of ports (``python -m
serial.tools.list_ports``). It also contains the following functions.
.. function:: comports(include_links=False)
:param bool include_links: include symlinks under ``/dev`` when they point
to a serial port
:return: a list containing :class:`ListPortInfo` objects.
The function returns a list of :obj:`ListPortInfo` objects.
Items are returned in no particular order. It may make sense to sort the
items. Also note that the reported strings are different across platforms
and operating systems, even for the same device.
.. note:: Support is limited to a number of operating systems. On some
systems description and hardware ID will not be available
(``None``).
Under Linux, OSX and Windows, extended information will be available for
USB devices (e.g. the :attr:`ListPortInfo.hwid` string contains `VID:PID`,
`SER` (serial number), `LOCATION` (hierarchy), which makes them searchable
via :func:`grep`. The USB info is also available as attributes of
:attr:`ListPortInfo`.
If *include_links* is true, all devices under ``/dev`` are inspected and
tested if they are a link to a known serial port device. These entries
will include ``LINK`` in their ``hwid`` string. This implies that the same
device listed twice, once under its original name and once under linked
name.
:platform: Posix (/dev files)
:platform: Linux (/dev files, sysfs)
:platform: OSX (iokit)
:platform: Windows (setupapi, registry)
.. function:: grep(regexp, include_links=False)
:param regexp: regular expression (see stdlib :mod:`re`)
:param bool include_links: include symlinks under ``/dev`` when they point
to a serial port
:return: an iterable that yields :class:`ListPortInfo` objects, see also
:func:`comports`.
Search for ports using a regular expression. Port ``name``,
``description`` and ``hwid`` are searched (case insensitive). The function
returns an iterable that contains the same data that :func:`comports`
generates, but includes only those entries that match the regexp.
.. class:: ListPortInfo
This object holds information about a serial port. It supports indexed
access for backwards compatibility, as in ``port, desc, hwid = info``.
.. attribute:: device
Full device name/path, e.g. ``/dev/ttyUSB0``. This is also the
information returned as first element when accessed by index.
.. attribute:: name
Short device name, e.g. ``ttyUSB0``.
.. attribute:: description
Human readable description or ``n/a``. This is also the information
returned as second element when accessed by index.
.. attribute:: hwid
Technical description or ``n/a``. This is also the information
returned as third element when accessed by index.
USB specific data, these are all ``None`` if it is not an USB device (or the
platform does not support extended info).
.. attribute:: vid
USB Vendor ID (integer, 0...65535).
.. attribute:: pid
USB product ID (integer, 0...65535).
.. attribute:: serial_number
USB serial number as a string.
.. attribute:: location
USB device location string ("<bus>-<port>[-<port>]...")
.. attribute:: manufacturer
USB manufacturer string, as reported by device.
.. attribute:: product
USB product string, as reported by device.
.. attribute:: interface
Interface specific description, e.g. used in compound USB devices.
Comparison operators are implemented such that the :obj:`ListPortInfo` objects
can be sorted by ``device``. Strings are split into groups of numbers and
text so that the order is "natural" (i.e. ``com1`` < ``com2`` <
``com10``).
**Command line usage**
Help for ``python -m serial.tools.list_ports``::
usage: list_ports.py [-h] [-v] [-q] [-n N] [-s] [regexp]
Serial port enumeration
positional arguments:
regexp only show ports that match this regex
optional arguments:
-h, --help show this help message and exit
-v, --verbose show more messages
-q, --quiet suppress all messages
-n N only output the N-th entry
-s, --include-links include entries that are symlinks to real devices
Examples:
- List all ports with details::
$ python -m serial.tools.list_ports -v
/dev/ttyS0
desc: ttyS0
hwid: PNP0501
/dev/ttyUSB0
desc: CP2102 USB to UART Bridge Controller
hwid: USB VID:PID=10C4:EA60 SER=0001 LOCATION=2-1.6
2 ports found
- List the 2nd port matching a USB VID:PID pattern::
$ python -m serial.tools.list_ports 1234:5678 -q -n 2
/dev/ttyUSB1
.. versionadded:: 2.6
.. versionchanged:: 3.0 returning ``ListPortInfo`` objects instead of a tuple
.. _miniterm:
serial.tools.miniterm
=====================
.. module:: serial.tools.miniterm
This is a console application that provides a small terminal application.
Miniterm itself does not implement any terminal features such as VT102
compatibility. However it may inherit these features from the terminal it is run.
For example on GNU/Linux running from an xterm it will support the escape
sequences of the xterm. On Windows the typical console window is dumb and does
not support any escapes. When ANSI.sys is loaded it supports some escapes.
The default is to filter terminal control characters, see ``--filter`` for
different options.
Miniterm::
--- Miniterm on /dev/ttyS0: 9600,8,N,1 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
Command line options can be given so that binary data including escapes for
terminals are escaped or output as hex.
Miniterm supports :rfc:`2217` remote serial ports and raw sockets using :ref:`URLs`
such as ``rfc2217://<host>:<port>`` respectively ``socket://<host>:<port>`` as
*port* argument when invoking.
Command line options ``python -m serial.tools.miniterm -h``::
usage: miniterm.py [-h] [--parity {N,E,O,S,M}] [--rtscts] [--xonxoff]
[--rts RTS] [--dtr DTR] [-e] [--encoding CODEC] [-f NAME]
[--eol {CR,LF,CRLF}] [--raw] [--exit-char NUM]
[--menu-char NUM] [-q] [--develop]
[port] [baudrate]
Miniterm - A simple terminal program for the serial port.
positional arguments:
port serial port name
baudrate set baud rate, default: 9600
optional arguments:
-h, --help show this help message and exit
port settings:
--parity {N,E,O,S,M} set parity, one of {N E O S M}, default: N
--rtscts enable RTS/CTS flow control (default off)
--xonxoff enable software flow control (default off)
--rts RTS set initial RTS line state (possible values: 0, 1)
--dtr DTR set initial DTR line state (possible values: 0, 1)
--ask ask again for port when open fails
data handling:
-e, --echo enable local echo (default off)
--encoding CODEC set the encoding for the serial port (e.g. hexlify,
Latin1, UTF-8), default: UTF-8
-f NAME, --filter NAME
add text transformation
--eol {CR,LF,CRLF} end of line mode
--raw Do no apply any encodings/transformations
hotkeys:
--exit-char NUM Unicode of special character that is used to exit the
application, default: 29
--menu-char NUM Unicode code of special character that is used to
control miniterm (menu), default: 20
diagnostics:
-q, --quiet suppress non-error messages
--develop show Python traceback on error
Available filters (``--filter`` option):
- ``colorize``: Apply different colors for received and echo
- ``debug``: Print what is sent and received
- ``default``: remove typical terminal control codes from input
- ``direct``: do-nothing: forward all data unchanged
- ``nocontrol``: Remove all control codes, incl. ``CR+LF``
- ``printable``: Show decimal code for all non-ASCII characters and replace most control codes
Miniterm supports some control functions while being connected.
Typing :kbd:`Ctrl+T Ctrl+H` when it is running shows the help text::
--- pySerial (3.0a) - miniterm - help
---
--- Ctrl+] Exit program
--- Ctrl+T Menu escape key, followed by:
--- Menu keys:
--- Ctrl+T Send the menu character itself to remote
--- Ctrl+] Send the exit character itself to remote
--- Ctrl+I Show info
--- Ctrl+U Upload file (prompt will be shown)
--- Ctrl+A encoding
--- Ctrl+F edit filters
--- Toggles:
--- Ctrl+R RTS Ctrl+D DTR Ctrl+B BREAK
--- Ctrl+E echo Ctrl+L EOL
---
--- Port settings (Ctrl+T followed by the following):
--- p change port
--- 7 8 set data bits
--- N E O S M change parity (None, Even, Odd, Space, Mark)
--- 1 2 3 set stop bits (1, 2, 1.5)
--- b change baud rate
--- x X disable/enable software flow control
--- r R disable/enable hardware flow control
:kbd:`Ctrl+T z` suspends the connection (port is opened) and reconnects when a
key is pressed. This can be used to temporarily access the serial port with an
other application, without exiting miniterm. If reconnecting fails it is
also possible to exit (:kbd:`Ctrl+]`) or change the port (:kbd:`p`).
.. versionchanged:: 2.5
Added :kbd:`Ctrl+T` menu and added support for opening URLs.
.. versionchanged:: 2.6
File moved from the examples to :mod:`serial.tools.miniterm`.
.. versionchanged:: 3.0
Apply encoding on serial port, convert to Unicode for console.
Added new filters, default to stripping terminal control sequences.
Added ``--ask`` option.
.. versionchanged:: 3.5
Enable escape code handling on Windows 10 console.
@@ -1,274 +0,0 @@
.. _URLs:
==============
URL Handlers
==============
.. module:: serial
Overview
========
The function :func:`serial_for_url` accepts the following types of URLs:
- ``rfc2217://<host>:<port>[?<option>[&<option>...]]``
- ``socket://<host>:<port>[?logging={debug|info|warning|error}]``
- ``loop://[?logging={debug|info|warning|error}]``
- ``hwgrep://<regexp>[&skip_busy][&n=N]``
- ``spy://port[?option[=value][&option[=value]]]``
- ``alt://port?class=<classname>``
- ``cp2110://<bus>:<dev>:<if>``
.. versionchanged:: 3.0 Options are specified with ``?`` and ``&`` instead of ``/``
Device names are also supported, e.g.:
- ``/dev/ttyUSB0`` (Linux)
- ``COM3`` (Windows)
Future releases of pySerial might add more types. Since pySerial 2.6 it is also
possible for the user to add protocol handlers using
:attr:`protocol_handler_packages`.
``rfc2217://``
==============
Used to connect to :rfc:`2217` compatible servers. All serial port
functions are supported. Implemented by :class:`rfc2217.Serial`.
Supported options in the URL are:
- ``ign_set_control`` does not wait for acknowledges to SET_CONTROL. This
option can be used for non compliant servers (i.e. when getting an
``remote rejected value for option 'control'`` error when connecting).
- ``poll_modem``: The client issues NOTIFY_MODEMSTATE requests when status
lines are read (CTS/DTR/RI/CD). Without this option it relies on the server
sending the notifications automatically (that's what the RFC suggests and
most servers do). Enable this option when :attr:`cts` does not work as
expected, i.e. for servers that do not send notifications.
- ``timeout=<value>``: Change network timeout (default 3 seconds). This is
useful when the server takes a little more time to send its answers. The
timeout applies to the initial Telnet / :rfc:`2217` negotiation as well
as changing port settings or control line change commands.
- ``logging={debug|info|warning|error}``: Prints diagnostic messages (not
useful for end users). It uses the logging module and a logger called
``pySerial.rfc2217`` so that the application can setup up logging
handlers etc. It will call :meth:`logging.basicConfig` which initializes
for output on ``sys.stderr`` (if no logging was set up already).
.. warning:: The connection is not encrypted and no authentication is
supported! Only use it in trusted environments.
``socket://``
=============
The purpose of this connection type is that applications using pySerial can
connect to TCP/IP to serial port converters that do not support :rfc:`2217`.
Uses a TCP/IP socket. All serial port settings, control and status lines
are ignored. Only data is transmitted and received.
Supported options in the URL are:
- ``logging={debug|info|warning|error}``: Prints diagnostic messages (not
useful for end users). It uses the logging module and a logger called
``pySerial.socket`` so that the application can setup up logging handlers
etc. It will call :meth:`logging.basicConfig` which initializes for
output on ``sys.stderr`` (if no logging was set up already).
.. warning:: The connection is not encrypted and no authentication is
supported! Only use it in trusted environments.
``loop://``
===========
The least useful type. It simulates a loop back connection
(``RX<->TX`` ``RTS<->CTS`` ``DTR<->DSR``). It could be used to test
applications or run the unit tests.
Supported options in the URL are:
- ``logging={debug|info|warning|error}``: Prints diagnostic messages (not
useful for end users). It uses the logging module and a logger called
``pySerial.loop`` so that the application can setup up logging handlers
etc. It will call :meth:`logging.basicConfig` which initializes for
output on ``sys.stderr`` (if no logging was set up already).
``hwgrep://``
=============
This type uses :mod:`serial.tools.list_ports` to obtain a list of ports and
searches the list for matches by a regexp that follows the slashes (see Pythons
:py:mod:`re` module for detailed syntax information).
Note that options are separated using the character ``&``, this also applies to
the first, where URLs usually use ``?``. This exception is made as the question
mark is used in regexp itself.
Depending on the capabilities of the ``list_ports`` module on the system, it is
possible to search for the description or hardware ID of a device, e.g. USB
VID:PID or texts.
Unfortunately, on some systems ``list_ports`` only lists a subset of the port
names with no additional information. Currently, on Windows and Linux and
OSX it should find additional information.
Supported options in the URL are:
- ``n=N``: pick the N'th entry instead of the first
- ``skip_busy``: skip ports that can not be opened, e.g. because they are
already in use. This may not work as expected on platforms where the file is
not locked automatically (e.g. Posix).
.. _spy:
``spy://``
==========
Wrapping the native serial port, this protocol makes it possible to
intercept the data received and transmitted as well as the access to the
control lines, break and flush commands. It is mainly used to debug
applications.
Supported options in the URL are:
- ``file=FILENAME`` output to given file or device instead of stderr
- ``color`` enable ANSI escape sequences to colorize output
- ``raw`` output the read and written data directly (default is to create a
hex dump). In this mode, no control line and other commands are logged.
- ``all`` also show ``in_waiting`` and empty ``read()`` calls (hidden by
default because of high traffic).
- ``log`` or ``log=LOGGERNAME`` output to stdlib ``logging`` module. Default
channel name is ``serial``. This variant outputs hex dump.
- ``rawlog`` or ``rawlog=LOGGERNAME`` output to stdlib ``logging`` module. Default
channel name is ``serial``. This variant outputs text (``repr``).
The ``log`` and ``rawlog`` options require that the logging is set up, in order
to see the log output.
Example::
import serial
with serial.serial_for_url('spy:///dev/ttyUSB0?file=test.txt', timeout=1) as s:
s.dtr = False
s.write('hello world')
s.read(20)
s.dtr = True
s.write(serial.to_bytes(range(256)))
s.read(400)
s.send_break()
with open('test.txt') as f:
print(f.read())
Outputs::
000000.002 Q-RX reset_input_buffer
000000.002 DTR inactive
000000.002 TX 0000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world
000001.015 RX 0000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world
000001.015 DTR active
000001.015 TX 0000 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ................
000001.015 TX 0010 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F ................
000001.015 TX 0020 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F !"#$%&'()*+,-./
000001.015 TX 0030 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 0123456789:;<=>?
000001.015 TX 0040 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F @ABCDEFGHIJKLMNO
000001.016 TX 0050 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F PQRSTUVWXYZ[\]^_
000001.016 TX 0060 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F `abcdefghijklmno
000001.016 TX 0070 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F pqrstuvwxyz{|}~.
000001.016 TX 0080 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F ................
000001.016 TX 0090 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F ................
000001.016 TX 00A0 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF ................
000001.016 TX 00B0 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF ................
000001.016 TX 00C0 C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF ................
000001.016 TX 00D0 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF ................
000001.016 TX 00E0 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF ................
000001.016 TX 00F0 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................
000002.284 RX 0000 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ................
000002.284 RX 0010 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F ................
000002.284 RX 0020 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F !"#$%&'()*+,-./
000002.284 RX 0030 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 0123456789:;<=>?
000002.284 RX 0040 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F @ABCDEFGHIJKLMNO
000002.284 RX 0050 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F PQRSTUVWXYZ[\]^_
000002.284 RX 0060 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F `abcdefghijklmno
000002.284 RX 0070 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F pqrstuvwxyz{|}~.
000002.284 RX 0080 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F ................
000002.284 RX 0090 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F ................
000002.284 RX 00A0 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF ................
000002.284 RX 00B0 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF ................
000002.284 RX 00C0 C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF ................
000002.284 RX 00D0 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF ................
000002.284 RX 00E0 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF ................
000002.284 RX 00F0 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................
000002.284 BRK send_break 0.25
Another example, on POSIX, open a second terminal window and find out it's
device (e.g. with the ``ps`` command in the TTY column), assumed to be
``/dev/pts/2`` here, double quotes are used so that the ampersand in the URL is
not interpreted by the shell::
python -m serial.tools.miniterm "spy:///dev/ttyUSB0?file=/dev/pts/2&color" 115200
The spy output will be live in the second terminal window.
.. versionadded:: 3.0
.. versionchanged:: 3.6 Added ``log`` and ``rawlog`` options
``alt://``
==========
This handler allows to select alternate implementations of the native serial
port.
Currently only the POSIX platform provides alternative implementations.
``PosixPollSerial``
Poll based read implementation. Not all systems support poll properly.
However this one has better handling of errors, such as a device
disconnecting while it's in use (e.g. USB-serial unplugged).
``VTIMESerial``
Implement timeout using ``VTIME``/``VMIN`` of TTY device instead of using
``select``. This means that inter character timeout and overall timeout
can not be used at the same time. Overall timeout is disabled when
inter-character timeout is used. The error handling is degraded.
Examples::
alt:///dev/ttyUSB0?class=PosixPollSerial
alt:///dev/ttyUSB0?class=VTIMESerial
.. versionadded:: 3.0
``cp2110://``
=============
This backend implements support for HID-to-UART devices manufactured by Silicon
Labs and marketed as CP2110 and CP2114. The implementation is (mostly)
OS-independent and in userland. It relies on `cython-hidapi`_.
.. _cython-hidapi: https://github.com/trezor/cython-hidapi
Examples::
cp2110://0001:004a:00
cp2110://0002:0077:00
.. versionadded:: 3.5
Examples
========
- ``rfc2217://localhost:7000``
- ``rfc2217://localhost:7000?poll_modem``
- ``rfc2217://localhost:7000?ign_set_control&timeout=5.5``
- ``socket://localhost:7777``
- ``loop://?logging=debug``
- ``hwgrep://0451:f432`` (USB VID:PID)
- ``spy://COM54?file=log.txt``
- ``alt:///dev/ttyUSB0?class=PosixPollSerial``
- ``cp2110://0001:004a:00``
@@ -1,154 +0,0 @@
#! /usr/bin/env python
# encoding: utf-8
"""
Example of a AT command protocol.
https://en.wikipedia.org/wiki/Hayes_command_set
http://www.itu.int/rec/T-REC-V.250-200307-I/en
"""
from __future__ import print_function
import sys
sys.path.insert(0, '..')
import logging
import serial
import serial.threaded
import threading
try:
import queue
except ImportError:
import Queue as queue
class ATException(Exception):
pass
class ATProtocol(serial.threaded.LineReader):
TERMINATOR = b'\r\n'
def __init__(self):
super(ATProtocol, self).__init__()
self.alive = True
self.responses = queue.Queue()
self.events = queue.Queue()
self._event_thread = threading.Thread(target=self._run_event)
self._event_thread.daemon = True
self._event_thread.name = 'at-event'
self._event_thread.start()
self.lock = threading.Lock()
def stop(self):
"""
Stop the event processing thread, abort pending commands, if any.
"""
self.alive = False
self.events.put(None)
self.responses.put('<exit>')
def _run_event(self):
"""
Process events in a separate thread so that input thread is not
blocked.
"""
while self.alive:
try:
self.handle_event(self.events.get())
except:
logging.exception('_run_event')
def handle_line(self, line):
"""
Handle input from serial port, check for events.
"""
if line.startswith('+'):
self.events.put(line)
else:
self.responses.put(line)
def handle_event(self, event):
"""
Spontaneous message received.
"""
print('event received:', event)
def command(self, command, response='OK', timeout=5):
"""
Set an AT command and wait for the response.
"""
with self.lock: # ensure that just one thread is sending commands at once
self.write_line(command)
lines = []
while True:
try:
line = self.responses.get(timeout=timeout)
#~ print("%s -> %r" % (command, line))
if line == response:
return lines
else:
lines.append(line)
except queue.Empty:
raise ATException('AT command timeout ({!r})'.format(command))
# test
if __name__ == '__main__':
import time
class PAN1322(ATProtocol):
"""
Example communication with PAN1322 BT module.
Some commands do not respond with OK but with a '+...' line. This is
implemented via command_with_event_response and handle_event, because
'+...' lines are also used for real events.
"""
def __init__(self):
super(PAN1322, self).__init__()
self.event_responses = queue.Queue()
self._awaiting_response_for = None
def connection_made(self, transport):
super(PAN1322, self).connection_made(transport)
# our adapter enables the module with RTS=low
self.transport.serial.rts = False
time.sleep(0.3)
self.transport.serial.reset_input_buffer()
def handle_event(self, event):
"""Handle events and command responses starting with '+...'"""
if event.startswith('+RRBDRES') and self._awaiting_response_for.startswith('AT+JRBD'):
rev = event[9:9 + 12]
mac = ':'.join('{:02X}'.format(ord(x)) for x in rev.decode('hex')[::-1])
self.event_responses.put(mac)
else:
logging.warning('unhandled event: {!r}'.format(event))
def command_with_event_response(self, command):
"""Send a command that responds with '+...' line"""
with self.lock: # ensure that just one thread is sending commands at once
self._awaiting_response_for = command
self.transport.write(b'{}\r\n'.format(command.encode(self.ENCODING, self.UNICODE_HANDLING)))
response = self.event_responses.get()
self._awaiting_response_for = None
return response
# - - - example commands
def reset(self):
self.command("AT+JRES", response='ROK') # SW-Reset BT module
def get_mac_address(self):
# requests hardware / calibration info as event
return self.command_with_event_response("AT+JRBD")
ser = serial.serial_for_url('spy://COM1', baudrate=115200, timeout=1)
#~ ser = serial.Serial('COM1', baudrate=115200, timeout=1)
with serial.threaded.ReaderThread(ser, PAN1322) as bt_module:
bt_module.reset()
print("reset OK")
print("MAC address is", bt_module.get_mac_address())
@@ -1,578 +0,0 @@
#! /usr/bin/env python
#
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Multi-port serial<->TCP/IP forwarder.
- RFC 2217
- check existence of serial port periodically
- start/stop forwarders
- each forwarder creates a server socket and opens the serial port
- serial ports are opened only once. network connect/disconnect
does not influence serial port
- only one client per connection
"""
import os
import select
import socket
import sys
import time
import traceback
import serial
import serial.rfc2217
import serial.tools.list_ports
import dbus
# Try to import the avahi service definitions properly. If the avahi module is
# not available, fall back to a hard-coded solution that hopefully still works.
try:
import avahi
except ImportError:
class avahi:
DBUS_NAME = "org.freedesktop.Avahi"
DBUS_PATH_SERVER = "/"
DBUS_INTERFACE_SERVER = "org.freedesktop.Avahi.Server"
DBUS_INTERFACE_ENTRY_GROUP = DBUS_NAME + ".EntryGroup"
IF_UNSPEC = -1
PROTO_UNSPEC, PROTO_INET, PROTO_INET6 = -1, 0, 1
class ZeroconfService:
"""\
A simple class to publish a network service with zeroconf using avahi.
"""
def __init__(self, name, port, stype="_http._tcp",
domain="", host="", text=""):
self.name = name
self.stype = stype
self.domain = domain
self.host = host
self.port = port
self.text = text
self.group = None
def publish(self):
bus = dbus.SystemBus()
server = dbus.Interface(
bus.get_object(
avahi.DBUS_NAME,
avahi.DBUS_PATH_SERVER
),
avahi.DBUS_INTERFACE_SERVER
)
g = dbus.Interface(
bus.get_object(
avahi.DBUS_NAME,
server.EntryGroupNew()
),
avahi.DBUS_INTERFACE_ENTRY_GROUP
)
g.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0),
self.name, self.stype, self.domain, self.host,
dbus.UInt16(self.port), self.text)
g.Commit()
self.group = g
def unpublish(self):
if self.group is not None:
self.group.Reset()
self.group = None
def __str__(self):
return "{!r} @ {}:{} ({})".format(self.name, self.host, self.port, self.stype)
class Forwarder(ZeroconfService):
"""\
Single port serial<->TCP/IP forarder that depends on an external select
loop.
- Buffers for serial -> network and network -> serial
- RFC 2217 state
- Zeroconf publish/unpublish on open/close.
"""
def __init__(self, device, name, network_port, on_close=None, log=None):
ZeroconfService.__init__(self, name, network_port, stype='_serial_port._tcp')
self.alive = False
self.network_port = network_port
self.on_close = on_close
self.log = log
self.device = device
self.serial = serial.Serial()
self.serial.port = device
self.serial.baudrate = 115200
self.serial.timeout = 0
self.socket = None
self.server_socket = None
self.rfc2217 = None # instantiate later, when connecting
def __del__(self):
try:
if self.alive:
self.close()
except:
pass # XXX errors on shutdown
def open(self):
"""open serial port, start network server and publish service"""
self.buffer_net2ser = bytearray()
self.buffer_ser2net = bytearray()
# open serial port
try:
self.serial.rts = False
self.serial.open()
except Exception as msg:
self.handle_serial_error(msg)
self.serial_settings_backup = self.serial.get_settings()
# start the socket server
# XXX add IPv6 support: use getaddrinfo for socket options, bind to multiple sockets?
# info_list = socket.getaddrinfo(None, port, 0, socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(
socket.SOL_SOCKET,
socket.SO_REUSEADDR,
self.server_socket.getsockopt(
socket.SOL_SOCKET,
socket.SO_REUSEADDR
) | 1
)
self.server_socket.setblocking(0)
try:
self.server_socket.bind(('', self.network_port))
self.server_socket.listen(1)
except socket.error as msg:
self.handle_server_error()
#~ raise
if self.log is not None:
self.log.info("{}: Waiting for connection on {}...".format(self.device, self.network_port))
# zeroconfig
self.publish()
# now we are ready
self.alive = True
def close(self):
"""Close all resources and unpublish service"""
if self.log is not None:
self.log.info("{}: closing...".format(self.device))
self.alive = False
self.unpublish()
if self.server_socket:
self.server_socket.close()
if self.socket:
self.handle_disconnect()
self.serial.close()
if self.on_close is not None:
# ensure it is only called once
callback = self.on_close
self.on_close = None
callback(self)
def write(self, data):
"""the write method is used by serial.rfc2217.PortManager. it has to
write to the network."""
self.buffer_ser2net += data
def update_select_maps(self, read_map, write_map, error_map):
"""Update dictionaries for select call. insert fd->callback mapping"""
if self.alive:
# always handle serial port reads
read_map[self.serial] = self.handle_serial_read
error_map[self.serial] = self.handle_serial_error
# handle serial port writes if buffer is not empty
if self.buffer_net2ser:
write_map[self.serial] = self.handle_serial_write
# handle network
if self.socket is not None:
# handle socket if connected
# only read from network if the internal buffer is not
# already filled. the TCP flow control will hold back data
if len(self.buffer_net2ser) < 2048:
read_map[self.socket] = self.handle_socket_read
# only check for write readiness when there is data
if self.buffer_ser2net:
write_map[self.socket] = self.handle_socket_write
error_map[self.socket] = self.handle_socket_error
else:
# no connection, ensure clear buffer
self.buffer_ser2net = bytearray()
# check the server socket
read_map[self.server_socket] = self.handle_connect
error_map[self.server_socket] = self.handle_server_error
def handle_serial_read(self):
"""Reading from serial port"""
try:
data = os.read(self.serial.fileno(), 1024)
if data:
# store data in buffer if there is a client connected
if self.socket is not None:
# escape outgoing data when needed (Telnet IAC (0xff) character)
if self.rfc2217:
data = serial.to_bytes(self.rfc2217.escape(data))
self.buffer_ser2net.extend(data)
else:
self.handle_serial_error()
except Exception as msg:
self.handle_serial_error(msg)
def handle_serial_write(self):
"""Writing to serial port"""
try:
# write a chunk
n = os.write(self.serial.fileno(), bytes(self.buffer_net2ser))
# and see how large that chunk was, remove that from buffer
self.buffer_net2ser = self.buffer_net2ser[n:]
except Exception as msg:
self.handle_serial_error(msg)
def handle_serial_error(self, error=None):
"""Serial port error"""
# terminate connection
self.close()
def handle_socket_read(self):
"""Read from socket"""
try:
# read a chunk from the serial port
data = self.socket.recv(1024)
if data:
# Process RFC 2217 stuff when enabled
if self.rfc2217:
data = b''.join(self.rfc2217.filter(data))
# add data to buffer
self.buffer_net2ser.extend(data)
else:
# empty read indicates disconnection
self.handle_disconnect()
except socket.error:
if self.log is not None:
self.log.exception("{}: error reading...".format(self.device))
self.handle_socket_error()
def handle_socket_write(self):
"""Write to socket"""
try:
# write a chunk
count = self.socket.send(bytes(self.buffer_ser2net))
# and remove the sent data from the buffer
self.buffer_ser2net = self.buffer_ser2net[count:]
except socket.error:
if self.log is not None:
self.log.exception("{}: error writing...".format(self.device))
self.handle_socket_error()
def handle_socket_error(self):
"""Socket connection fails"""
self.handle_disconnect()
def handle_connect(self):
"""Server socket gets a connection"""
# accept a connection in any case, close connection
# below if already busy
connection, addr = self.server_socket.accept()
if self.socket is None:
self.socket = connection
# More quickly detect bad clients who quit without closing the
# connection: After 1 second of idle, start sending TCP keep-alive
# packets every 1 second. If 3 consecutive keep-alive packets
# fail, assume the client is gone and close the connection.
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3)
self.socket.setblocking(0)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
if self.log is not None:
self.log.warning('{}: Connected by {}:{}'.format(self.device, addr[0], addr[1]))
self.serial.rts = True
self.serial.dtr = True
if self.log is not None:
self.rfc2217 = serial.rfc2217.PortManager(self.serial, self, logger=log.getChild(self.device))
else:
self.rfc2217 = serial.rfc2217.PortManager(self.serial, self)
else:
# reject connection if there is already one
connection.close()
if self.log is not None:
self.log.warning('{}: Rejecting connect from {}:{}'.format(self.device, addr[0], addr[1]))
def handle_server_error(self):
"""Socket server fails"""
self.close()
def handle_disconnect(self):
"""Socket gets disconnected"""
# signal disconnected terminal with control lines
try:
self.serial.rts = False
self.serial.dtr = False
finally:
# restore original port configuration in case it was changed
self.serial.apply_settings(self.serial_settings_backup)
# stop RFC 2217 state machine
self.rfc2217 = None
# clear send buffer
self.buffer_ser2net = bytearray()
# close network connection
if self.socket is not None:
self.socket.close()
self.socket = None
if self.log is not None:
self.log.warning('{}: Disconnected'.format(self.device))
def test():
service = ZeroconfService(name="TestService", port=3000)
service.publish()
input("Press the ENTER key to unpublish the service ")
service.unpublish()
if __name__ == '__main__': # noqa
import logging
import argparse
VERBOSTIY = [
logging.ERROR, # 0
logging.WARNING, # 1 (default)
logging.INFO, # 2
logging.DEBUG, # 3
]
parser = argparse.ArgumentParser(
usage="""\
%(prog)s [options]
Announce the existence of devices using zeroconf and provide
a TCP/IP <-> serial port gateway (implements RFC 2217).
If running as daemon, write to syslog. Otherwise write to stdout.
""",
epilog="""\
NOTE: no security measures are implemented. Anyone can remotely connect
to this service over the network.
Only one connection at once, per port, is supported. When the connection is
terminated, it waits for the next connect.
""")
group = parser.add_argument_group("serial port settings")
group.add_argument(
"--ports-regex",
help="specify a regex to search against the serial devices and their descriptions (default: %(default)s)",
default='/dev/ttyUSB[0-9]+',
metavar="REGEX")
group = parser.add_argument_group("network settings")
group.add_argument(
"--tcp-port",
dest="base_port",
help="specify lowest TCP port number (default: %(default)s)",
default=7000,
type=int,
metavar="PORT")
group = parser.add_argument_group("daemon")
group.add_argument(
"-d", "--daemon",
dest="daemonize",
action="store_true",
help="start as daemon",
default=False)
group.add_argument(
"--pidfile",
help="specify a name for the PID file",
default=None,
metavar="FILE")
group = parser.add_argument_group("diagnostics")
group.add_argument(
"-o", "--logfile",
help="write messages file instead of stdout",
default=None,
metavar="FILE")
group.add_argument(
"-q", "--quiet",
dest="verbosity",
action="store_const",
const=0,
help="suppress most diagnostic messages",
default=1)
group.add_argument(
"-v", "--verbose",
dest="verbosity",
action="count",
help="increase diagnostic messages")
args = parser.parse_args()
# set up logging
logging.basicConfig(level=VERBOSTIY[min(args.verbosity, len(VERBOSTIY) - 1)])
log = logging.getLogger('port_publisher')
# redirect output if specified
if args.logfile is not None:
class WriteFlushed:
def __init__(self, fileobj):
self.fileobj = fileobj
def write(self, s):
self.fileobj.write(s)
self.fileobj.flush()
def close(self):
self.fileobj.close()
sys.stdout = sys.stderr = WriteFlushed(open(args.logfile, 'a'))
# atexit.register(lambda: sys.stdout.close())
if args.daemonize:
# if running as daemon is requested, do the fork magic
# args.quiet = True
# do the UNIX double-fork magic, see Stevens' "Advanced
# Programming in the UNIX Environment" for details (ISBN 0201563177)
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as e:
log.critical("fork #1 failed: {} ({})\n".format(e.errno, e.strerror))
sys.exit(1)
# decouple from parent environment
os.chdir("/") # don't prevent unmounting....
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent, save eventual PID before
if args.pidfile is not None:
open(args.pidfile, 'w').write("{}".format(pid))
sys.exit(0)
except OSError as e:
log.critical("fork #2 failed: {} ({})\n".format(e.errno, e.strerror))
sys.exit(1)
if args.logfile is None:
import syslog
syslog.openlog("serial port publisher")
# redirect output to syslog
class WriteToSysLog:
def __init__(self):
self.buffer = ''
def write(self, s):
self.buffer += s
if '\n' in self.buffer:
output, self.buffer = self.buffer.split('\n', 1)
syslog.syslog(output)
def flush(self):
syslog.syslog(self.buffer)
self.buffer = ''
def close(self):
self.flush()
sys.stdout = sys.stderr = WriteToSysLog()
# ensure the that the daemon runs a normal user, if run as root
# if os.getuid() == 0:
# name, passwd, uid, gid, desc, home, shell = pwd.getpwnam('someuser')
# os.setgid(gid) # set group first
# os.setuid(uid) # set user
# keep the published stuff in a dictionary
published = {}
# get a nice hostname
hostname = socket.gethostname()
def unpublish(forwarder):
"""when forwarders die, we need to unregister them"""
try:
del published[forwarder.device]
except KeyError:
pass
else:
log.info("unpublish: {}".format(forwarder))
alive = True
next_check = 0
# main loop
while alive:
try:
# if it is time, check for serial port devices
now = time.time()
if now > next_check:
next_check = now + 5
connected = [d for d, p, i in serial.tools.list_ports.grep(args.ports_regex)]
# Handle devices that are published, but no longer connected
for device in set(published).difference(connected):
log.info("unpublish: {}".format(published[device]))
unpublish(published[device])
# Handle devices that are connected but not yet published
for device in sorted(set(connected).difference(published)):
# Find the first available port, starting from specified number
port = args.base_port
ports_in_use = [f.network_port for f in published.values()]
while port in ports_in_use:
port += 1
published[device] = Forwarder(
device,
"{} on {}".format(device, hostname),
port,
on_close=unpublish,
log=log)
log.warning("publish: {}".format(published[device]))
published[device].open()
# select_start = time.time()
read_map = {}
write_map = {}
error_map = {}
for publisher in published.values():
publisher.update_select_maps(read_map, write_map, error_map)
readers, writers, errors = select.select(
read_map.keys(),
write_map.keys(),
error_map.keys(),
5)
# select_end = time.time()
# print "select used %.3f s" % (select_end - select_start)
for reader in readers:
read_map[reader]()
for writer in writers:
write_map[writer]()
for error in errors:
error_map[error]()
# print "operation used %.3f s" % (time.time() - select_end)
except KeyboardInterrupt:
alive = False
sys.stdout.write('\n')
except SystemExit:
raise
except:
#~ raise
traceback.print_exc()
@@ -1,44 +0,0 @@
#! /bin/sh
# daemon starter script
# based on skeleton from Debian GNU/Linux
# cliechti at gmx.net
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/bin/port_publisher.py
NAME=port_publisher
DESC="serial port avahi device publisher"
test -f $DAEMON || exit 0
set -e
case "$1" in
start)
echo -n "Starting $DESC: "
$DAEMON --daemon --pidfile /var/run/$NAME.pid
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid
# \ --exec $DAEMON
echo "$NAME."
;;
restart|force-reload)
echo -n "Restarting $DESC: "
start-stop-daemon --stop --quiet --pidfile \
/var/run/$NAME.pid
# --exec $DAEMON
sleep 1
$DAEMON --daemon --pidfile /var/run/$NAME.pid
echo "$NAME."
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0
@@ -1,188 +0,0 @@
#!/usr/bin/env python
#
# redirect data from a TCP/IP connection to a serial port and vice versa
# using RFC 2217
#
# (C) 2009-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
import logging
import socket
import sys
import time
import threading
import serial
import serial.rfc2217
class Redirector(object):
def __init__(self, serial_instance, socket, debug=False):
self.serial = serial_instance
self.socket = socket
self._write_lock = threading.Lock()
self.rfc2217 = serial.rfc2217.PortManager(
self.serial,
self,
logger=logging.getLogger('rfc2217.server') if debug else None)
self.log = logging.getLogger('redirector')
def statusline_poller(self):
self.log.debug('status line poll thread started')
while self.alive:
time.sleep(1)
self.rfc2217.check_modem_lines()
self.log.debug('status line poll thread terminated')
def shortcircuit(self):
"""connect the serial port to the TCP port by copying everything
from one side to the other"""
self.alive = True
self.thread_read = threading.Thread(target=self.reader)
self.thread_read.daemon = True
self.thread_read.name = 'serial->socket'
self.thread_read.start()
self.thread_poll = threading.Thread(target=self.statusline_poller)
self.thread_poll.daemon = True
self.thread_poll.name = 'status line poll'
self.thread_poll.start()
self.writer()
def reader(self):
"""loop forever and copy serial->socket"""
self.log.debug('reader thread started')
while self.alive:
try:
data = self.serial.read(self.serial.in_waiting or 1)
if data:
# escape outgoing data when needed (Telnet IAC (0xff) character)
self.write(b''.join(self.rfc2217.escape(data)))
except socket.error as msg:
self.log.error('{}'.format(msg))
# probably got disconnected
break
self.alive = False
self.log.debug('reader thread terminated')
def write(self, data):
"""thread safe socket write with no data escaping. used to send telnet stuff"""
with self._write_lock:
self.socket.sendall(data)
def writer(self):
"""loop forever and copy socket->serial"""
while self.alive:
try:
data = self.socket.recv(1024)
if not data:
break
self.serial.write(b''.join(self.rfc2217.filter(data)))
except socket.error as msg:
self.log.error('{}'.format(msg))
# probably got disconnected
break
self.stop()
def stop(self):
"""Stop copying"""
self.log.debug('stopping')
if self.alive:
self.alive = False
self.thread_read.join()
self.thread_poll.join()
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
description="RFC 2217 Serial to Network (TCP/IP) redirector.",
epilog="""\
NOTE: no security measures are implemented. Anyone can remotely connect
to this service over the network.
Only one connection at once is supported. When the connection is terminated
it waits for the next connect.
""")
parser.add_argument('SERIALPORT')
parser.add_argument(
'-p', '--localport',
type=int,
help='local TCP port, default: %(default)s',
metavar='TCPPORT',
default=2217)
parser.add_argument(
'-v', '--verbose',
dest='verbosity',
action='count',
help='print more diagnostic messages (option can be given multiple times)',
default=0)
args = parser.parse_args()
if args.verbosity > 3:
args.verbosity = 3
level = (logging.WARNING,
logging.INFO,
logging.DEBUG,
logging.NOTSET)[args.verbosity]
logging.basicConfig(level=logging.INFO)
#~ logging.getLogger('root').setLevel(logging.INFO)
logging.getLogger('rfc2217').setLevel(level)
# connect to serial port
ser = serial.serial_for_url(args.SERIALPORT, do_not_open=True)
ser.timeout = 3 # required so that the reader thread can exit
# reset control line as no _remote_ "terminal" has been connected yet
ser.dtr = False
ser.rts = False
logging.info("RFC 2217 TCP/IP to Serial redirector - type Ctrl-C / BREAK to quit")
try:
ser.open()
except serial.SerialException as e:
logging.error("Could not open serial port {}: {}".format(ser.name, e))
sys.exit(1)
logging.info("Serving serial port: {}".format(ser.name))
settings = ser.get_settings()
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(('', args.localport))
srv.listen(1)
logging.info("TCP/IP port: {}".format(args.localport))
while True:
try:
client_socket, addr = srv.accept()
logging.info('Connected by {}:{}'.format(addr[0], addr[1]))
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
ser.rts = True
ser.dtr = True
# enter network <-> serial loop
r = Redirector(
ser,
client_socket,
args.verbosity > 0)
try:
r.shortcircuit()
finally:
logging.info('Disconnected')
r.stop()
client_socket.close()
ser.dtr = False
ser.rts = False
# Restore port settings (may have been changed by RFC 2217
# capable client)
ser.apply_settings(settings)
except KeyboardInterrupt:
sys.stdout.write('\n')
break
except socket.error as msg:
logging.error(str(msg))
logging.info('--- exit ---')
@@ -1,33 +0,0 @@
# setup script for py2exe to create the miniterm.exe
#
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from distutils.core import setup
import sys
sys.path.insert(0, '..')
import serial.tools.miniterm
sys.argv.extend("py2exe --bundle 1".split())
setup(
name='miniterm',
zipfile=None,
options={"py2exe": {
'dll_excludes': [],
'includes': [
'serial.urlhandler.protocol_hwgrep', 'serial.urlhandler.protocol_rfc2217',
'serial.urlhandler.protocol_socket', 'serial.urlhandler.protocol_loop'],
'dist_dir': 'bin',
'excludes': ['serialjava', 'serialposix', 'serialcli'],
'compressed': 1,
}
},
console=[
serial.tools.miniterm.__file__
],
)
@@ -1,31 +0,0 @@
# setup script for py2exe to create the rfc2217_server.exe
#
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from distutils.core import setup
import sys
import py2exe
sys.path.insert(0, '..')
sys.argv.extend("py2exe --bundle 1".split())
setup(
name='rfc2217_server',
zipfile=None,
options={"py2exe": {
'dll_excludes': [],
'includes': [
'serial.urlhandler.protocol_hwgrep', 'serial.urlhandler.protocol_rfc2217',
'serial.urlhandler.protocol_socket', 'serial.urlhandler.protocol_loop'],
'dist_dir': 'bin',
'excludes': ['serialjava', 'serialposix', 'serialcli'],
'compressed': 1,
},
},
console=[
"rfc2217_server.py",
],
)
@@ -1,41 +0,0 @@
# This is a setup.py example script for the use with py2exe
#
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from distutils.core import setup
import os
import sys
# this script is only useful for py2exe so just run that distutils command.
# that allows to run it with a simple double click.
sys.argv.append('py2exe')
# get an icon from somewhere.. the python installation should have one:
icon = os.path.join(os.path.dirname(sys.executable), 'py.ico')
setup(
options={
'py2exe': {
'excludes': ['javax.comm'],
'optimize': 2,
'dist_dir': 'dist',
}
},
name="wxTerminal",
windows=[
{
'script': "wxTerminal.py",
'icon_resources': [(0x0004, icon)]
},
],
zipfile="stuff.lib",
description="Simple serial terminal application",
version="0.1",
author="Chris Liechti",
author_email="cliechti@gmx.net",
url="https://github.com/pyserial/pyserial/",
)
@@ -1,230 +0,0 @@
#!/usr/bin/env python
#
# Redirect data from a TCP/IP connection to a serial port and vice versa.
#
# (C) 2002-2020 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
import sys
import socket
import serial
import serial.threaded
import time
class SerialToNet(serial.threaded.Protocol):
"""serial->socket"""
def __init__(self):
self.socket = None
def __call__(self):
return self
def data_received(self, data):
if self.socket is not None:
self.socket.sendall(data)
if __name__ == '__main__': # noqa
import argparse
parser = argparse.ArgumentParser(
description='Simple Serial to Network (TCP/IP) redirector.',
epilog="""\
NOTE: no security measures are implemented. Anyone can remotely connect
to this service over the network.
Only one connection at once is supported. When the connection is terminated
it waits for the next connect.
""")
parser.add_argument(
'SERIALPORT',
help="serial port name")
parser.add_argument(
'BAUDRATE',
type=int,
nargs='?',
help='set baud rate, default: %(default)s',
default=9600)
parser.add_argument(
'-q', '--quiet',
action='store_true',
help='suppress non error messages',
default=False)
parser.add_argument(
'--develop',
action='store_true',
help='Development mode, prints Python internals on errors',
default=False)
group = parser.add_argument_group('serial port')
group.add_argument(
"--bytesize",
choices=[5, 6, 7, 8],
type=int,
help="set bytesize, one of {5 6 7 8}, default: 8",
default=8)
group.add_argument(
"--parity",
choices=['N', 'E', 'O', 'S', 'M'],
type=lambda c: c.upper(),
help="set parity, one of {N E O S M}, default: N",
default='N')
group.add_argument(
"--stopbits",
choices=[1, 1.5, 2],
type=float,
help="set stopbits, one of {1 1.5 2}, default: 1",
default=1)
group.add_argument(
'--rtscts',
action='store_true',
help='enable RTS/CTS flow control (default off)',
default=False)
group.add_argument(
'--xonxoff',
action='store_true',
help='enable software flow control (default off)',
default=False)
group.add_argument(
'--rts',
type=int,
help='set initial RTS line state (possible values: 0, 1)',
default=None)
group.add_argument(
'--dtr',
type=int,
help='set initial DTR line state (possible values: 0, 1)',
default=None)
group = parser.add_argument_group('network settings')
exclusive_group = group.add_mutually_exclusive_group()
exclusive_group.add_argument(
'-P', '--localport',
type=int,
help='local TCP port',
default=7777)
exclusive_group.add_argument(
'-c', '--client',
metavar='HOST:PORT',
help='make the connection as a client, instead of running a server',
default=False)
args = parser.parse_args()
# connect to serial port
ser = serial.serial_for_url(args.SERIALPORT, do_not_open=True)
ser.baudrate = args.BAUDRATE
ser.bytesize = args.bytesize
ser.parity = args.parity
ser.stopbits = args.stopbits
ser.rtscts = args.rtscts
ser.xonxoff = args.xonxoff
if args.rts is not None:
ser.rts = args.rts
if args.dtr is not None:
ser.dtr = args.dtr
if not args.quiet:
sys.stderr.write(
'--- TCP/IP to Serial redirect on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'
'--- type Ctrl-C / BREAK to quit\n'.format(p=ser))
try:
ser.open()
except serial.SerialException as e:
sys.stderr.write('Could not open serial port {}: {}\n'.format(ser.name, e))
sys.exit(1)
ser_to_net = SerialToNet()
serial_worker = serial.threaded.ReaderThread(ser, ser_to_net)
serial_worker.start()
if not args.client:
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(('', args.localport))
srv.listen(1)
try:
intentional_exit = False
while True:
if args.client:
host, port = args.client.split(':')
sys.stderr.write("Opening connection to {}:{}...\n".format(host, port))
client_socket = socket.socket()
try:
client_socket.connect((host, int(port)))
except socket.error as msg:
sys.stderr.write('WARNING: {}\n'.format(msg))
time.sleep(5) # intentional delay on reconnection as client
continue
sys.stderr.write('Connected\n')
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
#~ client_socket.settimeout(5)
else:
sys.stderr.write('Waiting for connection on {}...\n'.format(args.localport))
client_socket, addr = srv.accept()
sys.stderr.write('Connected by {}\n'.format(addr))
# More quickly detect bad clients who quit without closing the
# connection: After 1 second of idle, start sending TCP keep-alive
# packets every 1 second. If 3 consecutive keep-alive packets
# fail, assume the client is gone and close the connection.
try:
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1)
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3)
client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
except AttributeError:
pass # XXX not available on windows
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
try:
ser_to_net.socket = client_socket
# enter network <-> serial loop
while True:
try:
data = client_socket.recv(1024)
if not data:
break
ser.write(data) # get a bunch of bytes and send them
except socket.error as msg:
if args.develop:
raise
sys.stderr.write('ERROR: {}\n'.format(msg))
# probably got disconnected
break
except KeyboardInterrupt:
intentional_exit = True
raise
except socket.error as msg:
if args.develop:
raise
sys.stderr.write('ERROR: {}\n'.format(msg))
finally:
ser_to_net.socket = None
sys.stderr.write('Disconnected\n')
client_socket.close()
if args.client and not intentional_exit:
time.sleep(5) # intentional delay on reconnection as client
except KeyboardInterrupt:
pass
sys.stderr.write('\n--- exit ---\n')
serial_worker.stop()
@@ -1,293 +0,0 @@
#!/usr/bin/env python
#
# A serial port configuration dialog for wxPython. A number of flags can
# be used to configure the fields that are displayed.
#
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
import wx
import serial
import serial.tools.list_ports
SHOW_BAUDRATE = 1 << 0
SHOW_FORMAT = 1 << 1
SHOW_FLOW = 1 << 2
SHOW_TIMEOUT = 1 << 3
SHOW_ALL = SHOW_BAUDRATE | SHOW_FORMAT | SHOW_FLOW | SHOW_TIMEOUT
class SerialConfigDialog(wx.Dialog):
"""\
Serial Port configuration dialog, to be used with pySerial 2.0+
When instantiating a class of this dialog, then the "serial" keyword
argument is mandatory. It is a reference to a serial.Serial instance.
the optional "show" keyword argument can be used to show/hide different
settings. The default is SHOW_ALL which corresponds to
SHOW_BAUDRATE|SHOW_FORMAT|SHOW_FLOW|SHOW_TIMEOUT. All constants can be
found in this module (not the class).
"""
def __init__(self, *args, **kwds):
# grab the serial keyword and remove it from the dict
self.serial = kwds['serial']
del kwds['serial']
self.show = SHOW_ALL
if 'show' in kwds:
self.show = kwds.pop('show')
# begin wxGlade: SerialConfigDialog.__init__
kwds["style"] = wx.DEFAULT_DIALOG_STYLE
wx.Dialog.__init__(self, *args, **kwds)
self.label_2 = wx.StaticText(self, -1, "Port")
self.choice_port = wx.Choice(self, -1, choices=[])
self.label_1 = wx.StaticText(self, -1, "Baudrate")
self.combo_box_baudrate = wx.ComboBox(self, -1, choices=[], style=wx.CB_DROPDOWN)
self.sizer_1_staticbox = wx.StaticBox(self, -1, "Basics")
self.panel_format = wx.Panel(self, -1)
self.label_3 = wx.StaticText(self.panel_format, -1, "Data Bits")
self.choice_databits = wx.Choice(self.panel_format, -1, choices=["choice 1"])
self.label_4 = wx.StaticText(self.panel_format, -1, "Stop Bits")
self.choice_stopbits = wx.Choice(self.panel_format, -1, choices=["choice 1"])
self.label_5 = wx.StaticText(self.panel_format, -1, "Parity")
self.choice_parity = wx.Choice(self.panel_format, -1, choices=["choice 1"])
self.sizer_format_staticbox = wx.StaticBox(self.panel_format, -1, "Data Format")
self.panel_timeout = wx.Panel(self, -1)
self.checkbox_timeout = wx.CheckBox(self.panel_timeout, -1, "Use Timeout")
self.text_ctrl_timeout = wx.TextCtrl(self.panel_timeout, -1, "")
self.label_6 = wx.StaticText(self.panel_timeout, -1, "seconds")
self.sizer_timeout_staticbox = wx.StaticBox(self.panel_timeout, -1, "Timeout")
self.panel_flow = wx.Panel(self, -1)
self.checkbox_rtscts = wx.CheckBox(self.panel_flow, -1, "RTS/CTS")
self.checkbox_xonxoff = wx.CheckBox(self.panel_flow, -1, "Xon/Xoff")
self.sizer_flow_staticbox = wx.StaticBox(self.panel_flow, -1, "Flow Control")
self.button_ok = wx.Button(self, wx.ID_OK, "")
self.button_cancel = wx.Button(self, wx.ID_CANCEL, "")
self.__set_properties()
self.__do_layout()
# end wxGlade
# attach the event handlers
self.__attach_events()
def __set_properties(self):
# begin wxGlade: SerialConfigDialog.__set_properties
self.SetTitle("Serial Port Configuration")
self.choice_databits.SetSelection(0)
self.choice_stopbits.SetSelection(0)
self.choice_parity.SetSelection(0)
self.text_ctrl_timeout.Enable(False)
self.button_ok.SetDefault()
# end wxGlade
self.SetTitle("Serial Port Configuration")
if self.show & SHOW_TIMEOUT:
self.text_ctrl_timeout.Enable(0)
self.button_ok.SetDefault()
if not self.show & SHOW_BAUDRATE:
self.label_1.Hide()
self.combo_box_baudrate.Hide()
if not self.show & SHOW_FORMAT:
self.panel_format.Hide()
if not self.show & SHOW_TIMEOUT:
self.panel_timeout.Hide()
if not self.show & SHOW_FLOW:
self.panel_flow.Hide()
# fill in ports and select current setting
preferred_index = 0
self.choice_port.Clear()
self.ports = []
for n, (portname, desc, hwid) in enumerate(sorted(serial.tools.list_ports.comports())):
self.choice_port.Append(u'{} - {}'.format(portname, desc))
self.ports.append(portname)
if self.serial.name == portname:
preferred_index = n
self.choice_port.SetSelection(preferred_index)
if self.show & SHOW_BAUDRATE:
preferred_index = None
# fill in baud rates and select current setting
self.combo_box_baudrate.Clear()
for n, baudrate in enumerate(self.serial.BAUDRATES):
self.combo_box_baudrate.Append(str(baudrate))
if self.serial.baudrate == baudrate:
preferred_index = n
if preferred_index is not None:
self.combo_box_baudrate.SetSelection(preferred_index)
else:
self.combo_box_baudrate.SetValue(u'{}'.format(self.serial.baudrate))
if self.show & SHOW_FORMAT:
# fill in data bits and select current setting
self.choice_databits.Clear()
for n, bytesize in enumerate(self.serial.BYTESIZES):
self.choice_databits.Append(str(bytesize))
if self.serial.bytesize == bytesize:
index = n
self.choice_databits.SetSelection(index)
# fill in stop bits and select current setting
self.choice_stopbits.Clear()
for n, stopbits in enumerate(self.serial.STOPBITS):
self.choice_stopbits.Append(str(stopbits))
if self.serial.stopbits == stopbits:
index = n
self.choice_stopbits.SetSelection(index)
# fill in parities and select current setting
self.choice_parity.Clear()
for n, parity in enumerate(self.serial.PARITIES):
self.choice_parity.Append(str(serial.PARITY_NAMES[parity]))
if self.serial.parity == parity:
index = n
self.choice_parity.SetSelection(index)
if self.show & SHOW_TIMEOUT:
# set the timeout mode and value
if self.serial.timeout is None:
self.checkbox_timeout.SetValue(False)
self.text_ctrl_timeout.Enable(False)
else:
self.checkbox_timeout.SetValue(True)
self.text_ctrl_timeout.Enable(True)
self.text_ctrl_timeout.SetValue(str(self.serial.timeout))
if self.show & SHOW_FLOW:
# set the rtscts mode
self.checkbox_rtscts.SetValue(self.serial.rtscts)
# set the rtscts mode
self.checkbox_xonxoff.SetValue(self.serial.xonxoff)
def __do_layout(self):
# begin wxGlade: SerialConfigDialog.__do_layout
sizer_2 = wx.BoxSizer(wx.VERTICAL)
sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
self.sizer_flow_staticbox.Lower()
sizer_flow = wx.StaticBoxSizer(self.sizer_flow_staticbox, wx.HORIZONTAL)
self.sizer_timeout_staticbox.Lower()
sizer_timeout = wx.StaticBoxSizer(self.sizer_timeout_staticbox, wx.HORIZONTAL)
self.sizer_format_staticbox.Lower()
sizer_format = wx.StaticBoxSizer(self.sizer_format_staticbox, wx.VERTICAL)
grid_sizer_1 = wx.FlexGridSizer(3, 2, 0, 0)
self.sizer_1_staticbox.Lower()
sizer_1 = wx.StaticBoxSizer(self.sizer_1_staticbox, wx.VERTICAL)
sizer_basics = wx.FlexGridSizer(3, 2, 0, 0)
sizer_basics.Add(self.label_2, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
sizer_basics.Add(self.choice_port, 0, wx.EXPAND, 0)
sizer_basics.Add(self.label_1, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
sizer_basics.Add(self.combo_box_baudrate, 0, wx.EXPAND, 0)
sizer_basics.AddGrowableCol(1)
sizer_1.Add(sizer_basics, 0, wx.EXPAND, 0)
sizer_2.Add(sizer_1, 0, wx.EXPAND, 0)
grid_sizer_1.Add(self.label_3, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
grid_sizer_1.Add(self.choice_databits, 1, wx.EXPAND | wx.ALIGN_RIGHT, 0)
grid_sizer_1.Add(self.label_4, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
grid_sizer_1.Add(self.choice_stopbits, 1, wx.EXPAND | wx.ALIGN_RIGHT, 0)
grid_sizer_1.Add(self.label_5, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
grid_sizer_1.Add(self.choice_parity, 1, wx.EXPAND | wx.ALIGN_RIGHT, 0)
sizer_format.Add(grid_sizer_1, 1, wx.EXPAND, 0)
self.panel_format.SetSizer(sizer_format)
sizer_2.Add(self.panel_format, 0, wx.EXPAND, 0)
sizer_timeout.Add(self.checkbox_timeout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
sizer_timeout.Add(self.text_ctrl_timeout, 0, 0, 0)
sizer_timeout.Add(self.label_6, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
self.panel_timeout.SetSizer(sizer_timeout)
sizer_2.Add(self.panel_timeout, 0, wx.EXPAND, 0)
sizer_flow.Add(self.checkbox_rtscts, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
sizer_flow.Add(self.checkbox_xonxoff, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
sizer_flow.Add((10, 10), 1, wx.EXPAND, 0)
self.panel_flow.SetSizer(sizer_flow)
sizer_2.Add(self.panel_flow, 0, wx.EXPAND, 0)
sizer_3.Add(self.button_ok, 0, 0, 0)
sizer_3.Add(self.button_cancel, 0, 0, 0)
sizer_2.Add(sizer_3, 0, wx.ALL | wx.ALIGN_RIGHT, 4)
self.SetSizer(sizer_2)
sizer_2.Fit(self)
self.Layout()
# end wxGlade
def __attach_events(self):
self.button_ok.Bind(wx.EVT_BUTTON, self.OnOK)
self.button_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
if self.show & SHOW_TIMEOUT:
self.checkbox_timeout.Bind(wx.EVT_CHECKBOX, self.OnTimeout)
def OnOK(self, events):
success = True
self.serial.port = self.ports[self.choice_port.GetSelection()]
if self.show & SHOW_BAUDRATE:
try:
b = int(self.combo_box_baudrate.GetValue())
except ValueError:
with wx.MessageDialog(
self,
'Baudrate must be a numeric value',
'Value Error',
wx.OK | wx.ICON_ERROR) as dlg:
dlg.ShowModal()
success = False
else:
self.serial.baudrate = b
if self.show & SHOW_FORMAT:
self.serial.bytesize = self.serial.BYTESIZES[self.choice_databits.GetSelection()]
self.serial.stopbits = self.serial.STOPBITS[self.choice_stopbits.GetSelection()]
self.serial.parity = self.serial.PARITIES[self.choice_parity.GetSelection()]
if self.show & SHOW_FLOW:
self.serial.rtscts = self.checkbox_rtscts.GetValue()
self.serial.xonxoff = self.checkbox_xonxoff.GetValue()
if self.show & SHOW_TIMEOUT:
if self.checkbox_timeout.GetValue():
try:
self.serial.timeout = float(self.text_ctrl_timeout.GetValue())
except ValueError:
with wx.MessageDialog(
self,
'Timeout must be a numeric value',
'Value Error',
wx.OK | wx.ICON_ERROR) as dlg:
dlg.ShowModal()
success = False
else:
self.serial.timeout = None
if success:
self.EndModal(wx.ID_OK)
def OnCancel(self, events):
self.EndModal(wx.ID_CANCEL)
def OnTimeout(self, events):
if self.checkbox_timeout.GetValue():
self.text_ctrl_timeout.Enable(True)
else:
self.text_ctrl_timeout.Enable(False)
# end of class SerialConfigDialog
class MyApp(wx.App):
"""Test code"""
def OnInit(self):
wx.InitAllImageHandlers()
ser = serial.Serial()
print(ser)
# loop until cancel is pressed, old values are used as start for the next run
# show the different views, one after the other
# value are kept.
for flags in (SHOW_BAUDRATE, SHOW_FLOW, SHOW_FORMAT, SHOW_TIMEOUT, SHOW_ALL):
dialog_serial_cfg = SerialConfigDialog(None, -1, "", serial=ser, show=flags)
self.SetTopWindow(dialog_serial_cfg)
result = dialog_serial_cfg.ShowModal()
print(ser)
if result != wx.ID_OK:
break
# the user can play around with the values, CANCEL aborts the loop
while True:
dialog_serial_cfg = SerialConfigDialog(None, -1, "", serial=ser)
self.SetTopWindow(dialog_serial_cfg)
result = dialog_serial_cfg.ShowModal()
print(ser)
if result != wx.ID_OK:
break
return 0
# end of class MyApp
if __name__ == "__main__":
app = MyApp(0)
app.MainLoop()
@@ -1,252 +0,0 @@
<?xml version="1.0"?>
<!-- generated by wxGlade 0.6.5 on Wed Oct 14 18:12:11 2015 -->
<application path="wxSerialConfigDialog.py" name="app" class="MyApp" option="0" language="python" top_window="dialog_serial_cfg" encoding="ISO-8859-1" use_gettext="0" overwrite="0" use_new_namespace="1" for_version="2.8" is_template="0" indent_amount="4" indent_symbol="space" source_extension=".cpp" header_extension=".h">
<object class="SerialConfigDialog" name="dialog_serial_cfg" base="EditDialog">
<style>wxDEFAULT_DIALOG_STYLE</style>
<title>Serial Port Configuration</title>
<object class="wxBoxSizer" name="sizer_2" base="EditBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>0</option>
<object class="wxStaticBoxSizer" name="sizer_1" base="EditStaticBoxSizer">
<orient>wxVERTICAL</orient>
<label>Basics</label>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>0</option>
<object class="wxFlexGridSizer" name="sizer_basics" base="EditFlexGridSizer">
<hgap>0</hgap>
<rows>3</rows>
<growable_cols>1</growable_cols>
<cols>2</cols>
<vgap>0</vgap>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>4</border>
<option>0</option>
<object class="wxStaticText" name="label_2" base="EditStaticText">
<attribute>1</attribute>
<label>Port</label>
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>0</option>
<object class="wxChoice" name="choice_port" base="EditChoice">
<selection>0</selection>
<choices>
</choices>
</object>
</object>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>4</border>
<option>0</option>
<object class="wxStaticText" name="label_1" base="EditStaticText">
<attribute>1</attribute>
<label>Baudrate</label>
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>0</option>
<object class="wxComboBox" name="combo_box_baudrate" base="EditComboBox">
<selection>-1</selection>
<choices>
</choices>
</object>
</object>
</object>
</object>
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>0</option>
<object class="wxPanel" name="panel_format" base="EditPanel">
<style>wxTAB_TRAVERSAL</style>
<object class="wxStaticBoxSizer" name="sizer_format" base="EditStaticBoxSizer">
<orient>wxVERTICAL</orient>
<label>Data Format</label>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>1</option>
<object class="wxFlexGridSizer" name="grid_sizer_1" base="EditFlexGridSizer">
<hgap>0</hgap>
<rows>3</rows>
<cols>2</cols>
<vgap>0</vgap>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>4</border>
<option>1</option>
<object class="wxStaticText" name="label_3" base="EditStaticText">
<attribute>1</attribute>
<label>Data Bits</label>
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND|wxALIGN_RIGHT</flag>
<border>0</border>
<option>1</option>
<object class="wxChoice" name="choice_databits" base="EditChoice">
<selection>0</selection>
<choices>
<choice>choice 1</choice>
</choices>
</object>
</object>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>4</border>
<option>1</option>
<object class="wxStaticText" name="label_4" base="EditStaticText">
<attribute>1</attribute>
<label>Stop Bits</label>
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND|wxALIGN_RIGHT</flag>
<border>0</border>
<option>1</option>
<object class="wxChoice" name="choice_stopbits" base="EditChoice">
<selection>0</selection>
<choices>
<choice>choice 1</choice>
</choices>
</object>
</object>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>4</border>
<option>1</option>
<object class="wxStaticText" name="label_5" base="EditStaticText">
<attribute>1</attribute>
<label>Parity</label>
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND|wxALIGN_RIGHT</flag>
<border>0</border>
<option>1</option>
<object class="wxChoice" name="choice_parity" base="EditChoice">
<selection>0</selection>
<choices>
<choice>choice 1</choice>
</choices>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>0</option>
<object class="wxPanel" name="panel_timeout" base="EditPanel">
<style>wxTAB_TRAVERSAL</style>
<object class="wxStaticBoxSizer" name="sizer_timeout" base="EditStaticBoxSizer">
<orient>wxHORIZONTAL</orient>
<label>Timeout</label>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>4</border>
<option>0</option>
<object class="wxCheckBox" name="checkbox_timeout" base="EditCheckBox">
<label>Use Timeout</label>
</object>
</object>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxTextCtrl" name="text_ctrl_timeout" base="EditTextCtrl">
<disabled>1</disabled>
</object>
</object>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>4</border>
<option>0</option>
<object class="wxStaticText" name="label_6" base="EditStaticText">
<attribute>1</attribute>
<label>seconds</label>
</object>
</object>
</object>
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>0</option>
<object class="wxPanel" name="panel_flow" base="EditPanel">
<style>wxTAB_TRAVERSAL</style>
<object class="wxStaticBoxSizer" name="sizer_flow" base="EditStaticBoxSizer">
<orient>wxHORIZONTAL</orient>
<label>Flow Control</label>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>4</border>
<option>0</option>
<object class="wxCheckBox" name="checkbox_rtscts" base="EditCheckBox">
<label>RTS/CTS</label>
</object>
</object>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>4</border>
<option>0</option>
<object class="wxCheckBox" name="checkbox_xonxoff" base="EditCheckBox">
<label>Xon/Xoff</label>
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>1</option>
<object class="spacer" name="spacer" base="EditSpacer">
<height>10</height>
<width>10</width>
</object>
</object>
</object>
</object>
</object>
<object class="sizeritem">
<flag>wxALL|wxALIGN_RIGHT</flag>
<border>4</border>
<option>0</option>
<object class="wxBoxSizer" name="sizer_3" base="EditBoxSizer">
<orient>wxHORIZONTAL</orient>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxButton" name="button_ok" base="EditButton">
<stockitem>OK</stockitem>
<default>1</default>
<label>&amp;OK</label>
</object>
</object>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxButton" name="button_cancel" base="EditButton">
<stockitem>CANCEL</stockitem>
<label>&amp;Cancel</label>
</object>
</object>
</object>
</object>
</object>
</object>
</application>
@@ -1,367 +0,0 @@
#!/usr/bin/env python
#
# A simple terminal application with wxPython.
#
# (C) 2001-2020 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
import codecs
from serial.tools.miniterm import unichr
import serial
import threading
import wx
import wx.lib.newevent
import wxSerialConfigDialog
try:
unichr
except NameError:
unichr = chr
# ----------------------------------------------------------------------
# Create an own event type, so that GUI updates can be delegated
# this is required as on some platforms only the main thread can
# access the GUI without crashing. wxMutexGuiEnter/wxMutexGuiLeave
# could be used too, but an event is more elegant.
SerialRxEvent, EVT_SERIALRX = wx.lib.newevent.NewEvent()
SERIALRX = wx.NewEventType()
# ----------------------------------------------------------------------
ID_CLEAR = wx.NewId()
ID_SAVEAS = wx.NewId()
ID_SETTINGS = wx.NewId()
ID_TERM = wx.NewId()
ID_EXIT = wx.NewId()
ID_RTS = wx.NewId()
ID_DTR = wx.NewId()
NEWLINE_CR = 0
NEWLINE_LF = 1
NEWLINE_CRLF = 2
class TerminalSetup:
"""
Placeholder for various terminal settings. Used to pass the
options to the TerminalSettingsDialog.
"""
def __init__(self):
self.echo = False
self.unprintable = False
self.newline = NEWLINE_CRLF
class TerminalSettingsDialog(wx.Dialog):
"""Simple dialog with common terminal settings like echo, newline mode."""
def __init__(self, *args, **kwds):
self.settings = kwds['settings']
del kwds['settings']
# begin wxGlade: TerminalSettingsDialog.__init__
kwds["style"] = wx.DEFAULT_DIALOG_STYLE
wx.Dialog.__init__(self, *args, **kwds)
self.checkbox_echo = wx.CheckBox(self, -1, "Local Echo")
self.checkbox_unprintable = wx.CheckBox(self, -1, "Show unprintable characters")
self.radio_box_newline = wx.RadioBox(self, -1, "Newline Handling", choices=["CR only", "LF only", "CR+LF"], majorDimension=0, style=wx.RA_SPECIFY_ROWS)
self.sizer_4_staticbox = wx.StaticBox(self, -1, "Input/Output")
self.button_ok = wx.Button(self, wx.ID_OK, "")
self.button_cancel = wx.Button(self, wx.ID_CANCEL, "")
self.__set_properties()
self.__do_layout()
# end wxGlade
self.__attach_events()
self.checkbox_echo.SetValue(self.settings.echo)
self.checkbox_unprintable.SetValue(self.settings.unprintable)
self.radio_box_newline.SetSelection(self.settings.newline)
def __set_properties(self):
# begin wxGlade: TerminalSettingsDialog.__set_properties
self.SetTitle("Terminal Settings")
self.radio_box_newline.SetSelection(0)
self.button_ok.SetDefault()
# end wxGlade
def __do_layout(self):
# begin wxGlade: TerminalSettingsDialog.__do_layout
sizer_2 = wx.BoxSizer(wx.VERTICAL)
sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
self.sizer_4_staticbox.Lower()
sizer_4 = wx.StaticBoxSizer(self.sizer_4_staticbox, wx.VERTICAL)
sizer_4.Add(self.checkbox_echo, 0, wx.ALL, 4)
sizer_4.Add(self.checkbox_unprintable, 0, wx.ALL, 4)
sizer_4.Add(self.radio_box_newline, 0, 0, 0)
sizer_2.Add(sizer_4, 0, wx.EXPAND, 0)
sizer_3.Add(self.button_ok, 0, 0, 0)
sizer_3.Add(self.button_cancel, 0, 0, 0)
sizer_2.Add(sizer_3, 0, wx.ALL | wx.ALIGN_RIGHT, 4)
self.SetSizer(sizer_2)
sizer_2.Fit(self)
self.Layout()
# end wxGlade
def __attach_events(self):
self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.button_ok.GetId())
self.Bind(wx.EVT_BUTTON, self.OnCancel, id=self.button_cancel.GetId())
def OnOK(self, events):
"""Update data with new values and close dialog."""
self.settings.echo = self.checkbox_echo.GetValue()
self.settings.unprintable = self.checkbox_unprintable.GetValue()
self.settings.newline = self.radio_box_newline.GetSelection()
self.EndModal(wx.ID_OK)
def OnCancel(self, events):
"""Do not update data but close dialog."""
self.EndModal(wx.ID_CANCEL)
# end of class TerminalSettingsDialog
class TerminalFrame(wx.Frame):
"""Simple terminal program for wxPython"""
def __init__(self, *args, **kwds):
self.serial = serial.Serial()
self.serial.timeout = 0.5 # make sure that the alive event can be checked from time to time
self.settings = TerminalSetup() # placeholder for the settings
self.thread = None
self.alive = threading.Event()
# begin wxGlade: TerminalFrame.__init__
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
# Menu Bar
self.frame_terminal_menubar = wx.MenuBar()
wxglade_tmp_menu = wx.Menu()
wxglade_tmp_menu.Append(ID_CLEAR, "&Clear", "", wx.ITEM_NORMAL)
wxglade_tmp_menu.Append(ID_SAVEAS, "&Save Text As...", "", wx.ITEM_NORMAL)
wxglade_tmp_menu.AppendSeparator()
wxglade_tmp_menu.Append(ID_TERM, "&Terminal Settings...", "", wx.ITEM_NORMAL)
wxglade_tmp_menu.AppendSeparator()
wxglade_tmp_menu.Append(ID_EXIT, "&Exit", "", wx.ITEM_NORMAL)
self.frame_terminal_menubar.Append(wxglade_tmp_menu, "&File")
wxglade_tmp_menu = wx.Menu()
wxglade_tmp_menu.Append(ID_RTS, "RTS", "", wx.ITEM_CHECK)
wxglade_tmp_menu.Append(ID_DTR, "&DTR", "", wx.ITEM_CHECK)
wxglade_tmp_menu.Append(ID_SETTINGS, "&Port Settings...", "", wx.ITEM_NORMAL)
self.frame_terminal_menubar.Append(wxglade_tmp_menu, "Serial Port")
self.SetMenuBar(self.frame_terminal_menubar)
# Menu Bar end
self.text_ctrl_output = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE | wx.TE_READONLY)
self.__set_properties()
self.__do_layout()
self.Bind(wx.EVT_MENU, self.OnClear, id=ID_CLEAR)
self.Bind(wx.EVT_MENU, self.OnSaveAs, id=ID_SAVEAS)
self.Bind(wx.EVT_MENU, self.OnTermSettings, id=ID_TERM)
self.Bind(wx.EVT_MENU, self.OnExit, id=ID_EXIT)
self.Bind(wx.EVT_MENU, self.OnRTS, id=ID_RTS)
self.Bind(wx.EVT_MENU, self.OnDTR, id=ID_DTR)
self.Bind(wx.EVT_MENU, self.OnPortSettings, id=ID_SETTINGS)
# end wxGlade
self.__attach_events() # register events
self.OnPortSettings(None) # call setup dialog on startup, opens port
if not self.alive.is_set():
self.Close()
def StartThread(self):
"""Start the receiver thread"""
self.thread = threading.Thread(target=self.ComPortThread)
self.thread.daemon = True
self.alive.set()
self.thread.start()
self.serial.rts = True
self.serial.dtr = True
self.frame_terminal_menubar.Check(ID_RTS, self.serial.rts)
self.frame_terminal_menubar.Check(ID_DTR, self.serial.dtr)
def StopThread(self):
"""Stop the receiver thread, wait until it's finished."""
if self.thread is not None:
self.alive.clear() # clear alive event for thread
self.thread.join() # wait until thread has finished
self.thread = None
def __set_properties(self):
# begin wxGlade: TerminalFrame.__set_properties
self.SetTitle("Serial Terminal")
self.SetSize((546, 383))
self.text_ctrl_output.SetFont(wx.Font(9, wx.MODERN, wx.NORMAL, wx.NORMAL, 0, ""))
# end wxGlade
def __do_layout(self):
# begin wxGlade: TerminalFrame.__do_layout
sizer_1 = wx.BoxSizer(wx.VERTICAL)
sizer_1.Add(self.text_ctrl_output, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
self.Layout()
# end wxGlade
def __attach_events(self):
# register events at the controls
self.Bind(wx.EVT_MENU, self.OnClear, id=ID_CLEAR)
self.Bind(wx.EVT_MENU, self.OnSaveAs, id=ID_SAVEAS)
self.Bind(wx.EVT_MENU, self.OnExit, id=ID_EXIT)
self.Bind(wx.EVT_MENU, self.OnPortSettings, id=ID_SETTINGS)
self.Bind(wx.EVT_MENU, self.OnTermSettings, id=ID_TERM)
self.text_ctrl_output.Bind(wx.EVT_CHAR, self.OnKey)
self.Bind(wx.EVT_CHAR_HOOK, self.OnKey)
self.Bind(EVT_SERIALRX, self.OnSerialRead)
self.Bind(wx.EVT_CLOSE, self.OnClose)
def OnExit(self, event): # wxGlade: TerminalFrame.<event_handler>
"""Menu point Exit"""
self.Close()
def OnClose(self, event):
"""Called on application shutdown."""
self.StopThread() # stop reader thread
self.serial.close() # cleanup
self.Destroy() # close windows, exit app
def OnSaveAs(self, event): # wxGlade: TerminalFrame.<event_handler>
"""Save contents of output window."""
with wx.FileDialog(
None,
"Save Text As...",
".",
"",
"Text File|*.txt|All Files|*",
wx.SAVE) as dlg:
if dlg.ShowModal() == wx.ID_OK:
filename = dlg.GetPath()
with codecs.open(filename, 'w', encoding='utf-8') as f:
text = self.text_ctrl_output.GetValue().encode("utf-8")
f.write(text)
def OnClear(self, event): # wxGlade: TerminalFrame.<event_handler>
"""Clear contents of output window."""
self.text_ctrl_output.Clear()
def OnPortSettings(self, event): # wxGlade: TerminalFrame.<event_handler>
"""
Show the port settings dialog. The reader thread is stopped for the
settings change.
"""
if event is not None: # will be none when called on startup
self.StopThread()
self.serial.close()
ok = False
while not ok:
with wxSerialConfigDialog.SerialConfigDialog(
self,
-1,
"",
show=wxSerialConfigDialog.SHOW_BAUDRATE | wxSerialConfigDialog.SHOW_FORMAT | wxSerialConfigDialog.SHOW_FLOW,
serial=self.serial) as dialog_serial_cfg:
dialog_serial_cfg.CenterOnParent()
result = dialog_serial_cfg.ShowModal()
# open port if not called on startup, open it on startup and OK too
if result == wx.ID_OK or event is not None:
try:
self.serial.open()
except serial.SerialException as e:
with wx.MessageDialog(self, str(e), "Serial Port Error", wx.OK | wx.ICON_ERROR)as dlg:
dlg.ShowModal()
else:
self.StartThread()
self.SetTitle("Serial Terminal on {} [{},{},{},{}{}{}]".format(
self.serial.portstr,
self.serial.baudrate,
self.serial.bytesize,
self.serial.parity,
self.serial.stopbits,
' RTS/CTS' if self.serial.rtscts else '',
' Xon/Xoff' if self.serial.xonxoff else '',
))
ok = True
else:
# on startup, dialog aborted
self.alive.clear()
ok = True
def OnTermSettings(self, event): # wxGlade: TerminalFrame.<event_handler>
"""\
Menu point Terminal Settings. Show the settings dialog
with the current terminal settings.
"""
with TerminalSettingsDialog(self, -1, "", settings=self.settings) as dialog:
dialog.CenterOnParent()
dialog.ShowModal()
def OnKey(self, event):
"""\
Key event handler. If the key is in the ASCII range, write it to the
serial port. Newline handling and local echo is also done here.
"""
code = event.GetUnicodeKey()
# if code < 256: # XXX bug in some versions of wx returning only capital letters
# code = event.GetKeyCode()
if code == 13: # is it a newline? (check for CR which is the RETURN key)
if self.settings.echo: # do echo if needed
self.text_ctrl_output.AppendText('\n')
if self.settings.newline == NEWLINE_CR:
self.serial.write(b'\r') # send CR
elif self.settings.newline == NEWLINE_LF:
self.serial.write(b'\n') # send LF
elif self.settings.newline == NEWLINE_CRLF:
self.serial.write(b'\r\n') # send CR+LF
else:
char = unichr(code)
if self.settings.echo: # do echo if needed
self.WriteText(char)
self.serial.write(char.encode('UTF-8', 'replace')) # send the character
event.StopPropagation()
def WriteText(self, text):
if self.settings.unprintable:
text = ''.join([c if (c >= ' ' and c != '\x7f') else unichr(0x2400 + ord(c)) for c in text])
self.text_ctrl_output.AppendText(text)
def OnSerialRead(self, event):
"""Handle input from the serial port."""
self.WriteText(event.data.decode('UTF-8', 'replace'))
def ComPortThread(self):
"""\
Thread that handles the incoming traffic. Does the basic input
transformation (newlines) and generates an SerialRxEvent
"""
while self.alive.is_set():
b = self.serial.read(self.serial.in_waiting or 1)
if b:
# newline transformation
if self.settings.newline == NEWLINE_CR:
b = b.replace(b'\r', b'\n')
elif self.settings.newline == NEWLINE_LF:
pass
elif self.settings.newline == NEWLINE_CRLF:
b = b.replace(b'\r\n', b'\n')
wx.PostEvent(self, SerialRxEvent(data=b))
def OnRTS(self, event): # wxGlade: TerminalFrame.<event_handler>
self.serial.rts = event.IsChecked()
def OnDTR(self, event): # wxGlade: TerminalFrame.<event_handler>
self.serial.dtr = event.IsChecked()
# end of class TerminalFrame
class MyApp(wx.App):
def OnInit(self):
frame_terminal = TerminalFrame(None, -1, "")
self.SetTopWindow(frame_terminal)
frame_terminal.Show(True)
return 1
# end of class MyApp
if __name__ == "__main__":
app = MyApp(0)
app.MainLoop()
@@ -1,154 +0,0 @@
<?xml version="1.0"?>
<!-- generated by wxGlade 0.6.5 on Wed Oct 14 18:12:48 2015 -->
<application path="wxTerminal.py" name="app" class="MyApp" option="0" language="python" top_window="frame_terminal" encoding="ISO-8859-1" use_gettext="0" overwrite="0" use_new_namespace="1" for_version="2.8" is_template="0" indent_amount="4" indent_symbol="space" source_extension=".cpp" header_extension=".h">
<object class="TerminalFrame" name="frame_terminal" base="EditFrame">
<style>wxDEFAULT_FRAME_STYLE</style>
<title>Serial Terminal</title>
<menubar>1</menubar>
<size>546, 383</size>
<object class="wxMenuBar" name="frame_terminal_menubar" base="EditMenuBar">
<menus>
<menu name="" label="&amp;File">
<item>
<label>&amp;Clear</label>
<id>ID_CLEAR</id>
<handler>OnClear</handler>
</item>
<item>
<label>&amp;Save Text As...</label>
<id>ID_SAVEAS</id>
<handler>OnSaveAs</handler>
</item>
<item>
<label>---</label>
<id>---</id>
<name>---</name>
</item>
<item>
<label>&amp;Terminal Settings...</label>
<id>ID_TERM</id>
<handler>OnTermSettings</handler>
</item>
<item>
<label>---</label>
<name>---</name>
</item>
<item>
<label>&amp;Exit</label>
<id>ID_EXIT</id>
<handler>OnExit</handler>
</item>
</menu>
<menu name="" label="Serial Port">
<item>
<label>RTS</label>
<id>ID_RTS</id>
<checkable>1</checkable>
<handler>OnRTS</handler>
</item>
<item>
<label>&amp;DTR</label>
<id>ID_DTR</id>
<checkable>1</checkable>
<handler>OnDTR</handler>
</item>
<item>
<label>&amp;Port Settings...</label>
<id>ID_SETTINGS</id>
<handler>OnPortSettings</handler>
</item>
</menu>
</menus>
</object>
<object class="wxBoxSizer" name="sizer_1" base="EditBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>1</option>
<object class="wxTextCtrl" name="text_ctrl_output" base="EditTextCtrl">
<style>wxTE_MULTILINE|wxTE_READONLY</style>
<font>
<size>9</size>
<family>modern</family>
<style>normal</style>
<weight>normal</weight>
<underlined>0</underlined>
<face></face>
</font>
</object>
</object>
</object>
</object>
<object class="TerminalSettingsDialog" name="dialog_terminal_Settings" base="EditDialog">
<style>wxDEFAULT_DIALOG_STYLE</style>
<title>Terminal Settings</title>
<object class="wxBoxSizer" name="sizer_2" base="EditBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>0</option>
<object class="wxStaticBoxSizer" name="sizer_4" base="EditStaticBoxSizer">
<orient>wxVERTICAL</orient>
<label>Input/Output</label>
<object class="sizeritem">
<flag>wxALL</flag>
<border>4</border>
<option>0</option>
<object class="wxCheckBox" name="checkbox_echo" base="EditCheckBox">
<label>Local Echo</label>
</object>
</object>
<object class="sizeritem">
<flag>wxALL</flag>
<border>4</border>
<option>0</option>
<object class="wxCheckBox" name="checkbox_unprintable" base="EditCheckBox">
<label>Show unprintable characters</label>
</object>
</object>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxRadioBox" name="radio_box_newline" base="EditRadioBox">
<style>wxRA_SPECIFY_ROWS</style>
<selection>0</selection>
<dimension>0</dimension>
<label>Newline Handling</label>
<choices>
<choice>CR only</choice>
<choice>LF only</choice>
<choice>CR+LF</choice>
</choices>
</object>
</object>
</object>
</object>
<object class="sizeritem">
<flag>wxALL|wxALIGN_RIGHT</flag>
<border>4</border>
<option>0</option>
<object class="wxBoxSizer" name="sizer_3" base="EditBoxSizer">
<orient>wxHORIZONTAL</orient>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxButton" name="button_ok" base="EditButton">
<stockitem>OK</stockitem>
<default>1</default>
</object>
</object>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxButton" name="button_cancel" base="EditButton">
<stockitem>CANCEL</stockitem>
</object>
</object>
</object>
</object>
</object>
</object>
</application>
-378
View File
@@ -1,378 +0,0 @@
[MASTER]
# Specify a configuration file.
#rcfile=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Pickle collected data for later comparisons.
persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Use multiple processes to speed up Pylint.
jobs=1
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=
# Allow optimization of some AST trees. This will activate a peephole AST
# optimizer, which will apply various small optimizations. For instance, it can
# be used to obtain the result of joining multiple strings with the addition
# operator. Joining a lot of strings can lead to a maximum recursion error in
# Pylint and this flag can prevent that. It has one side effect, the resulting
# AST will be different than the one from reality.
optimize-ast=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time. See also the "--disable" option for examples.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html. You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
reports=no
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
msg-template={path}:{line}: {msg_id} {symbol}, {obj} {msg}
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=4
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=120
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,dict-separator
# Maximum number of lines in a module
max-module-lines=1000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
[BASIC]
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,input
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_,n,z,c,b,e,ri,cd
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Regular expression matching correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Naming hint for function names
function-name-hint=[a-z_][a-z0-9_]{2,30}$
# Regular expression matching correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$
# Naming hint for variable names
variable-name-hint=[a-z_][a-z0-9_]{2,30}$
# Regular expression matching correct constant names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Naming hint for constant names
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression matching correct attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
# Naming hint for attribute names
attr-name-hint=[a-z_][a-z0-9_]{2,30}$
# Regular expression matching correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Naming hint for argument names
argument-name-hint=[a-z_][a-z0-9_]{2,30}$
# Regular expression matching correct class attribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Naming hint for class attribute names
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression matching correct inline iteration names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Naming hint for inline iteration names
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
# Regular expression matching correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Naming hint for class names
class-name-hint=[A-Z_][a-zA-Z0-9]+$
# Regular expression matching correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Naming hint for module names
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression matching correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Naming hint for method names
method-name-hint=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
[ELIF]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[TYPECHECK]
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=urllib,asyncio,msvcrt,queue,socket
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set). This supports can work
# with qualified names.
ignored-classes=
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_$|dummy
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of statements in function / method body
max-statements=60
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of attributes for a class (see R0902).
max-attributes=12
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of boolean expressions in a if statement
max-bool-expr=5
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,_fields,_replace,_source,_make
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception
@@ -1,7 +0,0 @@
[bdist_wheel]
universal=1
[flake8]
max-line-length = 120
ignore = E265, E126, E241
-108
View File
@@ -1,108 +0,0 @@
# setup.py for pySerial
#
# Direct install (all systems):
# "python setup.py install"
#
# For Python 3.x use the corresponding Python executable,
# e.g. "python3 setup.py ..."
#
# (C) 2001-2020 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
import io
import os
import re
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
def read(*names, **kwargs):
"""Python 2 and Python 3 compatible text file reading.
Required for single-sourcing the version string.
"""
with io.open(
os.path.join(os.path.dirname(__file__), *names),
encoding=kwargs.get("encoding", "utf8")
) as fp:
return fp.read()
def find_version(*file_paths):
"""
Search the file for a version string.
file_path contain string path components.
Reads the supplied Python module as text without importing it.
"""
version_file = read(*file_paths)
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
version_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string.")
version = find_version('serial', '__init__.py')
setup(
name="pyserial",
description="Python Serial Port Extension",
version=version,
author="Chris Liechti",
author_email="cliechti@gmx.net",
url="https://github.com/pyserial/pyserial",
packages=['serial', 'serial.tools', 'serial.urlhandler', 'serial.threaded'],
license="BSD",
long_description="""\
Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython
Stable:
- Documentation: http://pythonhosted.org/pyserial/
- Download Page: https://pypi.python.org/pypi/pyserial
Latest:
- Documentation: http://pyserial.readthedocs.io/en/latest/
- Project Homepage: https://github.com/pyserial/pyserial
""",
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Intended Audience :: End Users/Desktop',
'License :: OSI Approved :: BSD License',
'Natural Language :: English',
'Operating System :: POSIX',
'Operating System :: Microsoft :: Windows',
'Operating System :: MacOS :: MacOS X',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Topic :: Communications',
'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Terminals :: Serial',
],
platforms='any',
entry_points = {
'console_scripts': [
'pyserial-miniterm=serial.tools.miniterm:main',
'pyserial-ports=serial.tools.list_ports:main'
],
},
extras_require = {
'cp2110': ['hidapi'],
},
)
@@ -1,202 +0,0 @@
#! python
#
# Python Serial Port Extension for Win32, Linux, BSD, Jython
# see __init__.py
#
# This module implements a URL dummy handler for serial_for_url.
#
# (C) 2011 Chris Liechti <cliechti@gmx.net>
# this is distributed under a free software license, see license.txt
#
# URL format: test://
from serial.serialutil import *
import time
import socket
import logging
# map log level names to constants. used in fromURL()
LOGGER_LEVELS = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
}
class DummySerial(SerialBase):
"""Serial port implementation for plain sockets."""
def open(self):
"""Open port with current settings. This may throw a SerialException
if the port cannot be opened."""
self.logger = None
if self._port is None:
raise SerialException("Port must be configured before it can be used.")
# not that there anything to configure...
self._reconfigurePort()
# all things set up get, now a clean start
self._isOpen = True
def _reconfigurePort(self):
"""Set communication parameters on opened port. for the test://
protocol all settings are ignored!"""
if self.logger:
self.logger.info('ignored port configuration change')
def close(self):
"""Close port"""
if self._isOpen:
self._isOpen = False
def makeDeviceName(self, port):
raise SerialException("there is no sensible way to turn numbers into URLs")
def fromURL(self, url):
"""extract host and port from an URL string"""
if url.lower().startswith("test://"): url = url[7:]
try:
# is there a "path" (our options)?
if '/' in url:
# cut away options
url, options = url.split('/', 1)
# process options now, directly altering self
for option in options.split('/'):
if '=' in option:
option, value = option.split('=', 1)
else:
value = None
if option == 'logging':
logging.basicConfig() # XXX is that good to call it here?
self.logger = logging.getLogger('pySerial.test')
self.logger.setLevel(LOGGER_LEVELS[value])
self.logger.debug('enabled logging')
else:
raise ValueError('unknown option: {!r}'.format(option))
except ValueError as e:
raise SerialException('expected a string in the form "[test://][option[/option...]]": {}'.format(e))
return (host, port)
# - - - - - - - - - - - - - - - - - - - - - - - -
def inWaiting(self):
"""Return the number of characters currently in the input buffer."""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
# set this one to debug as the function could be called often...
self.logger.debug('WARNING: inWaiting returns dummy value')
return 0 # hmmm, see comment in read()
def read(self, size=1):
"""Read size bytes from the serial port. If a timeout is set it may
return less characters as requested. With no timeout it will block
until the requested number of bytes is read."""
if not self._isOpen: raise PortNotOpenError()
data = '123' # dummy data
return bytes(data)
def write(self, data):
"""Output the given string over the serial port. Can block if the
connection is blocked. May raise SerialException if the connection is
closed."""
if not self._isOpen: raise PortNotOpenError()
# nothing done
return len(data)
def flushInput(self):
"""Clear input buffer, discarding all that is in the buffer."""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('ignored flushInput')
def flushOutput(self):
"""Clear output buffer, aborting the current output and
discarding all that is in the buffer."""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('ignored flushOutput')
def sendBreak(self, duration=0.25):
"""Send break condition. Timed, returns to idle state after given
duration."""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('ignored sendBreak({!r})'.format(duration))
def setBreak(self, level=True):
"""Set break: Controls TXD. When active, to transmitting is
possible."""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('ignored setBreak({!r})'.format(level))
def setRTS(self, level=True):
"""Set terminal status line: Request To Send"""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('ignored setRTS({!r})'.format(level))
def setDTR(self, level=True):
"""Set terminal status line: Data Terminal Ready"""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('ignored setDTR({!r})'.format(level))
def getCTS(self):
"""Read terminal status line: Clear To Send"""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for getCTS()')
return True
def getDSR(self):
"""Read terminal status line: Data Set Ready"""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for getDSR()')
return True
def getRI(self):
"""Read terminal status line: Ring Indicator"""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for getRI()')
return False
def getCD(self):
"""Read terminal status line: Carrier Detect"""
if not self._isOpen: raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for getCD()')
return True
# - - - platform specific - - -
# None so far
# assemble Serial class with the platform specific implementation and the base
# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
# library, derive from io.RawIOBase
try:
import io
except ImportError:
# classic version with our own file-like emulation
class Serial(DummySerial, FileLike):
pass
else:
# io library present
class Serial(DummySerial, io.RawIOBase):
pass
# simple client test
if __name__ == '__main__':
import sys
s = Serial('test://logging=debug')
sys.stdout.write('{}\n'.format(s))
sys.stdout.write("write...\n")
s.write("hello\n")
s.flush()
sys.stdout.write("read: {}\n".format(s.read(5)))
s.close()
@@ -1,55 +0,0 @@
#! /usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
UnitTest runner. This one searches for all files named test_*.py and collects
all test cases from these files. Finally it runs all tests and prints a
summary.
"""
import unittest
import sys
import os
# inject local copy to avoid testing the installed version instead of the one in the repo
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import serial # noqa
print("Patching sys.path to test local version. Testing Version: {}".format(serial.VERSION))
PORT = 'loop://'
if len(sys.argv) > 1:
PORT = sys.argv[1]
# find files and the tests in them
mainsuite = unittest.TestSuite()
for modulename in [
os.path.splitext(x)[0]
for x in os.listdir(os.path.dirname(__file__) or '.')
if x != __file__ and x.startswith("test") and x.endswith(".py")
]:
try:
module = __import__(modulename)
except ImportError:
print("skipping {}".format(modulename))
else:
module.PORT = PORT
testsuite = unittest.findTestCases(module)
print("found {} tests in {!r}".format(testsuite.countTestCases(), modulename))
mainsuite.addTest(testsuite)
verbosity = 1
if '-v' in sys.argv[1:]:
verbosity = 2
print('-' * 78)
# run the collected tests
testRunner = unittest.TextTestRunner(verbosity=verbosity)
#~ testRunner = unittest.ConsoleTestRunner(verbosity=verbosity)
result = testRunner.run(mainsuite)
# set exit code accordingly to test results
sys.exit(not result.wasSuccessful())
@@ -1,233 +0,0 @@
#! /usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Some tests for the serial module.
Part of pyserial (http://pyserial.sf.net) (C)2001-2015 cliechti@gmx.net
Intended to be run on different platforms, to ensure portability of
the code.
For all these tests a simple hardware is required.
Loopback HW adapter:
Shortcut these pin pairs:
TX <-> RX
RTS <-> CTS
DTR <-> DSR
On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8)
"""
import unittest
import threading
import time
import sys
import serial
# on which port should the tests be performed:
PORT = 'loop://'
# indirection via bytearray b/c bytes(range(256)) does something else in Python 2.7
bytes_0to255 = bytes(bytearray(range(256)))
def segments(data, size=16):
for a in range(0, len(data), size):
yield data[a:a + size]
class Test4_Nonblocking(unittest.TestCase):
"""Test with timeouts"""
timeout = 0
def setUp(self):
self.s = serial.serial_for_url(PORT, timeout=self.timeout)
def tearDown(self):
self.s.close()
def test0_Messy(self):
"""NonBlocking (timeout=0)"""
# this is only here to write out the message in verbose mode
# because Test3 and Test4 print the same messages
def test1_ReadEmpty(self):
"""timeout: After port open, the input buffer must be empty"""
self.assertEqual(self.s.read(1), b'', "expected empty buffer")
def test2_Loopback(self):
"""timeout: each sent character should return (binary test).
this is also a test for the binary capability of a port."""
for block in segments(bytes_0to255):
length = len(block)
self.s.write(block)
# there might be a small delay until the character is ready (especially on win32)
time.sleep(0.05)
self.assertEqual(self.s.in_waiting, length, "expected exactly {} character for inWainting()".format(length))
self.assertEqual(self.s.read(length), block) #, "expected a %r which was written before" % block)
self.assertEqual(self.s.read(1), b'', "expected empty buffer after all sent chars are read")
def test2_LoopbackTimeout(self):
"""timeout: test the timeout/immediate return.
partial results should be returned."""
self.s.write(b"HELLO")
time.sleep(0.1) # there might be a small delay until the character is ready (especially on win32 and rfc2217)
# read more characters as are available to run in the timeout
self.assertEqual(self.s.read(10), b'HELLO', "expected the 'HELLO' which was written before")
self.assertEqual(self.s.read(1), b'', "expected empty buffer after all sent chars are read")
class Test3_Timeout(Test4_Nonblocking):
"""Same tests as the NonBlocking ones but this time with timeout"""
timeout = 1
def test0_Messy(self):
"""Blocking (timeout=1)"""
# this is only here to write out the message in verbose mode
# because Test3 and Test4 print the same messages
class SendEvent(threading.Thread):
def __init__(self, serial, delay=3):
threading.Thread.__init__(self)
self.serial = serial
self.delay = delay
self.x = threading.Event()
self.stopped = 0
self.start()
def run(self):
time.sleep(self.delay)
self.x.set()
if not self.stopped:
self.serial.write(b"E")
self.serial.flush()
def is_set(self):
return self.x.is_set()
def stop(self):
self.stopped = 1
self.x.wait()
class Test1_Forever(unittest.TestCase):
"""Tests a port with no timeout. These tests require that a
character is sent after some time to stop the test, this is done
through the SendEvent class and the Loopback HW."""
def setUp(self):
self.s = serial.serial_for_url(PORT, timeout=None)
self.event = SendEvent(self.s)
def tearDown(self):
self.event.stop()
self.s.close()
def test2_ReadEmpty(self):
"""no timeout: after port open, the input buffer must be empty (read).
a character is sent after some time to terminate the test (SendEvent)."""
c = self.s.read(1)
if not (self.event.is_set() and c == b'E'):
self.fail("expected marker (evt={!r}, c={!r})".format(self.event.is_set(), c))
class Test2_Forever(unittest.TestCase):
"""Tests a port with no timeout"""
def setUp(self):
self.s = serial.serial_for_url(PORT, timeout=None)
def tearDown(self):
self.s.close()
def test1_inWaitingEmpty(self):
"""no timeout: after port open, the input buffer must be empty (in_waiting)"""
self.assertEqual(self.s.in_waiting, 0, "expected empty buffer")
def test2_Loopback(self):
"""no timeout: each sent character should return (binary test).
this is also a test for the binary capability of a port."""
for block in segments(bytes_0to255):
length = len(block)
self.s.write(block)
# there might be a small delay until the character is ready (especially on win32 and rfc2217)
time.sleep(0.05)
self.assertEqual(self.s.in_waiting, length) #, "expected exactly %d character for inWainting()" % length)
self.assertEqual(self.s.read(length), block) #, "expected %r which was written before" % block)
self.assertEqual(self.s.in_waiting, 0, "expected empty buffer after all sent chars are read")
class Test0_DataWires(unittest.TestCase):
"""Test modem control lines"""
def setUp(self):
self.s = serial.serial_for_url(PORT)
def tearDown(self):
self.s.close()
def test1_RTS(self):
"""Test RTS/CTS"""
self.s.rts = False
time.sleep(1.1)
self.assertTrue(not self.s.cts, "CTS -> 0")
self.s.rts = True
time.sleep(1.1)
self.assertTrue(self.s.cts, "CTS -> 1")
def test2_DTR(self):
"""Test DTR/DSR"""
self.s.dtr = False
time.sleep(1.1)
self.assertTrue(not self.s.dsr, "DSR -> 0")
self.s.dtr = True
time.sleep(1.1)
self.assertTrue(self.s.dsr, "DSR -> 1")
def test3_RI(self):
"""Test RI"""
self.assertTrue(not self.s.ri, "RI -> 0")
class Test_MoreTimeouts(unittest.TestCase):
"""Test with timeouts"""
def setUp(self):
# create an closed serial port
self.s = serial.serial_for_url(PORT, do_not_open=True)
def tearDown(self):
self.s.reset_output_buffer()
self.s.flush()
#~ self.s.write(serial.XON)
self.s.close()
# reopen... some faulty USB-serial adapter make next test fail otherwise...
self.s.timeout = 1
self.s.xonxoff = False
self.s.open()
self.s.read(3000)
self.s.close()
def test_WriteTimeout(self):
"""Test write() timeout."""
# use xonxoff setting and the loop-back adapter to switch traffic on hold
self.s.port = PORT
self.s.write_timeout = 1.0
self.s.xonxoff = True
self.s.open()
self.s.write(serial.XOFF)
time.sleep(0.5) # some systems need a little delay so that they can react on XOFF
t1 = time.time()
self.assertRaises(serial.SerialTimeoutException, self.s.write, b"timeout please" * 200)
t2 = time.time()
self.assertTrue(0.9 <= (t2 - t1) < 2.1, "Timeout not in the given interval ({})".format(t2 - t1))
if __name__ == '__main__':
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,161 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Some tests for the serial module.
Part of pyserial (http://pyserial.sf.net) (C)2002 cliechti@gmx.net
Intended to be run on different platforms, to ensure portability of
the code.
These tests open a serial port and change all the settings on the fly.
If the port is really correctly configured cannot be determined - that
would require external hardware or a null modem cable and an other
serial port library... Thus it mainly tests that all features are
correctly implemented and that the interface does what it should.
"""
import unittest
import serial
# on which port should the tests be performed:
PORT = 'loop://'
class Test_ChangeAttributes(unittest.TestCase):
"""Test with timeouts"""
def setUp(self):
# create a closed serial port
self.s = serial.serial_for_url(PORT, do_not_open=True)
def tearDown(self):
self.s.close()
def test_PortSetting(self):
self.s.port = PORT
self.assertEqual(self.s.portstr.lower(), PORT.lower())
# test internals
self.assertEqual(self.s._port, PORT)
# test on the fly change
self.s.open()
self.assertTrue(self.s.isOpen())
def test_DoubleOpen(self):
self.s.open()
# calling open for a second time is an error
self.assertRaises(serial.SerialException, self.s.open)
def test_BaudrateSetting(self):
self.s.open()
for baudrate in (300, 9600, 19200, 115200):
self.s.baudrate = baudrate
# test get method
self.assertEqual(self.s.baudrate, baudrate)
# test internals
self.assertEqual(self.s._baudrate, baudrate)
# test illegal values
for illegal_value in (-300, -1, 'a', None):
self.assertRaises(ValueError, setattr, self.s, 'baudrate', illegal_value)
# skip this test as pyserial now tries to set even non standard baud rates.
# therefore the test can not choose a value that fails on any system.
def disabled_test_BaudrateSetting2(self):
# test illegal values, depending on machine/port some of these may be valid...
self.s.open()
for illegal_value in (500000, 576000, 921600, 92160):
self.assertRaises(ValueError, setattr, self.s, 'baudrate', illegal_value)
def test_BytesizeSetting(self):
for bytesize in (5, 6, 7, 8):
self.s.bytesize = bytesize
# test get method
self.assertEqual(self.s.bytesize, bytesize)
# test internals
self.assertEqual(self.s._bytesize, bytesize)
# test illegal values
for illegal_value in (0, 1, 3, 4, 9, 10, 'a', None):
self.assertRaises(ValueError, setattr, self.s, 'bytesize', illegal_value)
def test_ParitySetting(self):
for parity in (serial.PARITY_NONE, serial.PARITY_EVEN, serial.PARITY_ODD):
self.s.parity = parity
# test get method
self.assertEqual(self.s.parity, parity)
# test internals
self.assertEqual(self.s._parity, parity)
# test illegal values
for illegal_value in (0, 57, 'a', None):
self.assertRaises(ValueError, setattr, self.s, 'parity', illegal_value)
def test_StopbitsSetting(self):
for stopbits in (1, 2):
self.s.stopbits = stopbits
# test get method
self.assertEqual(self.s.stopbits, stopbits)
# test internals
self.assertEqual(self.s._stopbits, stopbits)
# test illegal values
for illegal_value in (0, 3, 2.5, 57, 'a', None):
self.assertRaises(ValueError, setattr, self.s, 'stopbits', illegal_value)
def test_TimeoutSetting(self):
for timeout in (None, 0, 1, 3.14159, 10, 1000, 3600):
self.s.timeout = timeout
# test get method
self.assertEqual(self.s.timeout, timeout)
# test internals
self.assertEqual(self.s._timeout, timeout)
# test illegal values
for illegal_value in (-1, 'a'):
self.assertRaises(ValueError, setattr, self.s, 'timeout', illegal_value)
def test_XonXoffSetting(self):
for xonxoff in (True, False):
self.s.xonxoff = xonxoff
# test get method
self.assertEqual(self.s.xonxoff, xonxoff)
# test internals
self.assertEqual(self.s._xonxoff, xonxoff)
# no illegal values here, normal rules for the boolean value of an
# object are used thus all objects have a truth value.
def test_RtsCtsSetting(self):
for rtscts in (True, False):
self.s.rtscts = rtscts
# test get method
self.assertEqual(self.s.rtscts, rtscts)
# test internals
self.assertEqual(self.s._rtscts, rtscts)
# no illegal values here, normal rules for the boolean value of an
# object are used thus all objects have a truth value.
# this test does not work anymore since serial_for_url that is used
# now, already sets a port
def disabled_test_UnconfiguredPort(self):
# an unconfigured port cannot be opened
self.assertRaises(serial.SerialException, self.s.open)
def test_PortOpenClose(self):
for i in range(3):
# open the port and check flag
self.assertTrue(not self.s.isOpen())
self.s.open()
self.assertTrue(self.s.isOpen())
self.s.close()
self.assertTrue(not self.s.isOpen())
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,82 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Test asyncio related functionality.
"""
import os
import unittest
import serial
# on which port should the tests be performed:
PORT = '/dev/ttyUSB0'
try:
import asyncio
import serial.aio
except (ImportError, SyntaxError):
# not compatible with python 2.x
pass
else:
@unittest.skipIf(os.name != 'posix', "asyncio not supported on platform")
class Test_asyncio(unittest.TestCase):
"""Test asyncio related functionality"""
def setUp(self):
self.loop = asyncio.get_event_loop()
# create a closed serial port
def tearDown(self):
self.loop.close()
def test_asyncio(self):
TEXT = b'hello world\n'
received = []
actions = []
class Output(asyncio.Protocol):
def connection_made(self, transport):
self.transport = transport
actions.append('open')
transport.serial.rts = False
transport.write(TEXT)
def data_received(self, data):
#~ print('data received', repr(data))
received.append(data)
if b'\n' in data:
self.transport.close()
def connection_lost(self, exc):
actions.append('close')
asyncio.get_event_loop().stop()
def pause_writing(self):
actions.append('pause')
print(self.transport.get_write_buffer_size())
def resume_writing(self):
actions.append('resume')
print(self.transport.get_write_buffer_size())
coro = serial.aio.create_serial_connection(self.loop, Output, PORT, baudrate=115200)
self.loop.run_until_complete(coro)
self.loop.run_forever()
self.assertEqual(b''.join(received), TEXT)
self.assertEqual(actions, ['open', 'close'])
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,109 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""
Test cancel functionality.
"""
import sys
import unittest
import threading
import time
import serial
# on which port should the tests be performed:
PORT = 'loop://'
@unittest.skipIf(not hasattr(serial.Serial, 'cancel_read'), "cancel_read not supported on platform")
class TestCancelRead(unittest.TestCase):
"""Test cancel_read functionality"""
def setUp(self):
# create a closed serial port
self.s = serial.serial_for_url(PORT)
self.assertTrue(hasattr(self.s, 'cancel_read'), "serial instance has no cancel_read")
self.s.timeout = 10
self.cancel_called = 0
def tearDown(self):
self.s.reset_output_buffer()
self.s.close()
def _cancel(self, num_times):
for i in range(num_times):
#~ print "cancel"
self.cancel_called += 1
self.s.cancel_read()
def test_cancel_once(self):
"""Cancel read"""
threading.Timer(1, self._cancel, ((1,))).start()
t1 = time.time()
self.s.read(1000)
t2 = time.time()
self.assertEqual(self.cancel_called, 1)
self.assertTrue(0.5 < (t2 - t1) < 2.5, 'Function did not return in time: {}'.format(t2 - t1))
#~ self.assertTrue(not self.s.isOpen())
#~ self.assertRaises(serial.SerialException, self.s.open)
#~ def test_cancel_before_read(self):
#~ self.s.cancel_read()
#~ self.s.read()
DATA = b'#' * 1024
@unittest.skipIf(not hasattr(serial.Serial, 'cancel_write'), "cancel_read not supported on platform")
class TestCancelWrite(unittest.TestCase):
"""Test cancel_write functionality"""
def setUp(self):
# create a closed serial port
self.s = serial.serial_for_url(PORT, baudrate=300) # extra slow ~30B/s => 1kb ~ 34s
self.assertTrue(hasattr(self.s, 'cancel_write'), "serial instance has no cancel_write")
self.s.write_timeout = 10
self.cancel_called = 0
def tearDown(self):
self.s.reset_output_buffer()
# not all USB-Serial adapters will actually flush the output (maybe
# keeping the buffer in the MCU in the adapter) therefore, speed up by
# changing the baudrate
self.s.baudrate = 115200
self.s.flush()
self.s.close()
def _cancel(self, num_times):
for i in range(num_times):
self.cancel_called += 1
self.s.cancel_write()
def test_cancel_once(self):
"""Cancel write"""
threading.Timer(1, self._cancel, ((1,))).start()
t1 = time.time()
self.s.write(DATA)
t2 = time.time()
self.assertEqual(self.cancel_called, 1)
self.assertTrue(0.5 < (t2 - t1) < 2.5, 'Function did not return in time: {}'.format(t2 - t1))
#~ self.assertTrue(not self.s.isOpen())
#~ self.assertRaises(serial.SerialException, self.s.open)
#~ def test_cancel_before_write(self):
#~ self.s.cancel_write()
#~ self.s.write(DATA)
#~ self.s.reset_output_buffer()
if __name__ == '__main__':
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,58 +0,0 @@
#! /usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
# (C) 2023 Google LLC
#
# SPDX-License-Identifier: BSD-3-Clause
import sys
import unittest
import serial
# on which port should the tests be performed:
PORT = 'loop://'
class TestClose(unittest.TestCase):
def test_closed_true(self):
# closed is True if a Serial port is not open
s = serial.Serial()
self.assertFalse(s.is_open)
self.assertTrue(s.closed)
def test_closed_false(self):
# closed is False if a Serial port is open
s = serial.serial_for_url(PORT, timeout=1)
self.assertTrue(s.is_open)
self.assertFalse(s.closed)
s.close()
self.assertTrue(s.closed)
def test_close_not_called_by_finalize_if_closed(self):
close_calls = 0
class TestSerial(serial.Serial):
def close(self):
nonlocal close_calls
close_calls += 1
with TestSerial() as s:
pass
# close() should be called here
# Trigger RawIOBase finalization.
# Because we override .closed, close() should not be called
# if Serial says it is already closed.
del s
self.assertEqual(close_calls, 1)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if __name__ == '__main__':
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,49 +0,0 @@
#! /usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2017 Guillaume Galeazzi <guillaume.g@leazzi.ch>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Some tests for the serial module.
Part of pySerial (http://pyserial.sf.net) (C)2001-2011 cliechti@gmx.net
Intended to be run on different platforms, to ensure portability of
the code.
Cover some of the aspects of context management
"""
import unittest
import serial
# on which port should the tests be performed:
PORT = 'loop://'
class Test_Context(unittest.TestCase):
"""Test context"""
def setUp(self):
# create a closed serial port
self.s = serial.serial_for_url(PORT)
def tearDown(self):
self.s.close()
def test_with_idempotent(self):
with self.s as stream:
stream.write(b'1234')
# do other stuff like calling an exe which use COM4
with self.s as stream:
stream.write(b'5678')
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,59 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2017 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Tests for exclusive access feature.
"""
import os
import unittest
import sys
import serial
# on which port should the tests be performed:
PORT = 'loop://'
class Test_exclusive(unittest.TestCase):
"""Test serial port locking"""
def setUp(self):
with serial.serial_for_url(PORT, do_not_open=True) as x:
if not isinstance(x, serial.Serial):
raise unittest.SkipTest("exclusive test only compatible with real serial port")
def test_exclusive_none(self):
"""test for exclusive=None"""
with serial.Serial(PORT, exclusive=None):
pass # OK
@unittest.skipUnless(os.name == 'posix', "exclusive=False not supported on platform")
def test_exclusive_false(self):
"""test for exclusive=False"""
with serial.Serial(PORT, exclusive=False):
pass # OK
@unittest.skipUnless(os.name in ('posix', 'nt'), "exclusive=True setting not supported on platform")
def test_exclusive_true(self):
"""test for exclusive=True"""
with serial.Serial(PORT, exclusive=True):
with self.assertRaises(serial.SerialException):
serial.Serial(PORT, exclusive=True) # fails to open twice
@unittest.skipUnless(os.name == 'nt', "platform is not restricted to exclusive=True (and None)")
def test_exclusive_only_true(self):
"""test if exclusive=False is not supported"""
with self.assertRaises(ValueError):
serial.Serial(PORT, exclusive=False) # expected to fail: False not supported
if __name__ == '__main__':
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,76 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""Some tests for the serial module.
Part of pyserial (http://pyserial.sf.net) (C)2002-2003 cliechti@gmx.net
Intended to be run on different platforms, to ensure portability of
the code.
For all these tests a simple hardware is required.
Loopback HW adapter:
Shortcut these pin pairs:
TX <-> RX
RTS <-> CTS
DTR <-> DSR
On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8)
"""
import unittest
import sys
import serial
# on which port should the tests be performed:
PORT = 'loop://'
BAUDRATE = 115200
#~ BAUDRATE=9600
if sys.version_info >= (3, 0):
bytes_0to255 = bytes(range(256))
else:
bytes_0to255 = ''.join([chr(x) for x in range(256)])
class TestHighLoad(unittest.TestCase):
"""Test sending and receiving large amount of data"""
N = 16
#~ N = 1
def setUp(self):
self.s = serial.serial_for_url(PORT, BAUDRATE, timeout=10)
def tearDown(self):
self.s.close()
def test0_WriteReadLoopback(self):
"""Send big strings, write/read order."""
for i in range(self.N):
q = bytes_0to255
self.s.write(q)
self.assertEqual(self.s.read(len(q)), q) # expected same which was written before
self.assertEqual(self.s.inWaiting(), 0) # expected empty buffer after all sent chars are read
def test1_WriteWriteReadLoopback(self):
"""Send big strings, multiple write one read."""
q = bytes_0to255
for i in range(self.N):
self.s.write(q)
read = self.s.read(len(q) * self.N)
self.assertEqual(read, q * self.N, "expected what was written before. got {} bytes, expected {}".format(len(read), self.N * len(q)))
self.assertEqual(self.s.inWaiting(), 0) # "expected empty buffer after all sent chars are read")
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,61 +0,0 @@
#! /usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Some tests for the serial module.
Part of pyserial (http://pyserial.sf.net) (C)2001-2009 cliechti@gmx.net
Intended to be run on different platforms, to ensure portability of
the code.
This modules contains test for the interaction between Serial and the io
library. This only works on Python 2.6+ that introduced the io library.
For all these tests a simple hardware is required.
Loopback HW adapter:
Shortcut these pin pairs:
TX <-> RX
RTS <-> CTS
DTR <-> DSR
On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8)
"""
import io
import sys
import unittest
import serial
# on which port should the tests be performed:
PORT = 'loop://'
class Test_SerialAndIO(unittest.TestCase):
def setUp(self):
self.s = serial.serial_for_url(PORT, timeout=1)
#~ self.io = io.TextIOWrapper(self.s)
self.io = io.TextIOWrapper(io.BufferedRWPair(self.s, self.s))
def tearDown(self):
self.s.close()
def test_hello_raw(self):
self.io.write(b"hello\n".decode('utf-8'))
self.io.flush() # it is buffering. required to get the data out
hello = self.io.readline()
self.assertEqual(hello, b"hello\n".decode('utf-8'))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,54 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""
Test PTY related functionality.
"""
import os
import sys
try:
import pty
except ImportError:
pty = None
import unittest
import serial
DATA = b'Hello\n'
@unittest.skipIf(pty is None, "pty module not supported on platform")
class Test_Pty_Serial_Open(unittest.TestCase):
"""Test PTY serial open"""
def setUp(self):
# Open PTY
self.master, self.slave = pty.openpty()
def test_pty_serial_open_slave(self):
with serial.Serial(os.ttyname(self.slave), timeout=1) as slave:
pass # OK
def test_pty_serial_write(self):
with serial.Serial(os.ttyname(self.slave), timeout=1) as slave:
with os.fdopen(self.master, "wb") as fd:
fd.write(DATA)
fd.flush()
out = slave.read(len(DATA))
self.assertEqual(DATA, out)
def test_pty_serial_read(self):
with serial.Serial(os.ttyname(self.slave), timeout=1) as slave:
with os.fdopen(self.master, "rb") as fd:
slave.write(DATA)
slave.flush()
out = fd.read(len(DATA))
self.assertEqual(DATA, out)
if __name__ == '__main__':
sys.stdout.write(__doc__)
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,104 +0,0 @@
#! /usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2010-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Some tests for the serial module.
Part of pyserial (http://pyserial.sf.net) (C)2010 cliechti@gmx.net
Intended to be run on different platforms, to ensure portability of
the code.
For all these tests a simple hardware is required.
Loopback HW adapter:
Shortcut these pin pairs:
TX <-> RX
RTS <-> CTS
DTR <-> DSR
On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8)
"""
import unittest
import sys
import serial
#~ print serial.VERSION
# on which port should the tests be performed:
PORT = 'loop://'
if sys.version_info >= (3, 0):
def data(string):
return bytes(string, 'latin1')
else:
def data(string):
return string
class Test_Readline(unittest.TestCase):
"""Test readline function"""
def setUp(self):
self.s = serial.serial_for_url(PORT, timeout=1)
def tearDown(self):
self.s.close()
def test_readline(self):
"""Test readline method"""
self.s.write(serial.to_bytes([0x31, 0x0a, 0x32, 0x0a, 0x33, 0x0a]))
self.assertEqual(self.s.readline(), serial.to_bytes([0x31, 0x0a]))
self.assertEqual(self.s.readline(), serial.to_bytes([0x32, 0x0a]))
self.assertEqual(self.s.readline(), serial.to_bytes([0x33, 0x0a]))
# this time we will get a timeout
self.assertEqual(self.s.readline(), serial.to_bytes([]))
def test_readlines(self):
"""Test readlines method"""
self.s.write(serial.to_bytes([0x31, 0x0a, 0x32, 0x0a, 0x33, 0x0a]))
self.assertEqual(
self.s.readlines(),
[serial.to_bytes([0x31, 0x0a]), serial.to_bytes([0x32, 0x0a]), serial.to_bytes([0x33, 0x0a])]
)
def test_xreadlines(self):
"""Test xreadlines method (skipped for io based systems)"""
if hasattr(self.s, 'xreadlines'):
self.s.write(serial.to_bytes([0x31, 0x0a, 0x32, 0x0a, 0x33, 0x0a]))
self.assertEqual(
list(self.s.xreadlines()),
[serial.to_bytes([0x31, 0x0a]), serial.to_bytes([0x32, 0x0a]), serial.to_bytes([0x33, 0x0a])]
)
def test_for_in(self):
"""Test for line in s"""
self.s.write(serial.to_bytes([0x31, 0x0a, 0x32, 0x0a, 0x33, 0x0a]))
lines = []
for line in self.s:
lines.append(line)
self.assertEqual(
lines,
[serial.to_bytes([0x31, 0x0a]), serial.to_bytes([0x32, 0x0a]), serial.to_bytes([0x33, 0x0a])]
)
def test_alternate_eol(self):
"""Test readline with alternative eol settings (skipped for io based systems)"""
if hasattr(self.s, 'xreadlines'): # test if it is our FileLike base class
self.s.write(serial.to_bytes("no\rno\nyes\r\n"))
self.assertEqual(
self.s.readline(eol=serial.to_bytes("\r\n")),
serial.to_bytes("no\rno\nyes\r\n"))
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,42 +0,0 @@
#! /usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Test RFC 2217 related functionality.
"""
import unittest
import serial
import serial.rfc2217
class Test_RFC2217(unittest.TestCase):
"""Test RFC 2217 related functionality"""
def test_failed_connection(self):
# connection to closed port
s = serial.serial_for_url('rfc2217://127.99.99.99:2217', do_not_open=True)
self.assertRaises(serial.SerialException, s.open)
self.assertFalse(s.is_open)
s.close() # no errors expected
# invalid address
s = serial.serial_for_url('rfc2217://127goingtofail', do_not_open=True)
self.assertRaises(serial.SerialException, s.open)
self.assertFalse(s.is_open)
s.close() # no errors expected
# close w/o open is also OK
s = serial.serial_for_url('rfc2217://irrelevant', do_not_open=True)
self.assertFalse(s.is_open)
s.close() # no errors expected
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
sys.stdout.write("Testing connection on localhost\n")
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,67 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Test RS485 related functionality.
"""
import unittest
import serial
import serial.rs485
# on which port should the tests be performed:
PORT = 'loop://'
class Test_RS485_settings(unittest.TestCase):
"""Test RS485 related functionality"""
def setUp(self):
# create a closed serial port
self.s = serial.serial_for_url(PORT, do_not_open=True)
def tearDown(self):
self.s.close()
def test_enable_RS485(self):
# XXX open() port - but will result in fail for most HW...
#~ self.s.open()
self.assertEqual(self.s._rs485_mode, None, 'RS485 is disabled by default')
self.assertEqual(self.s.rs485_mode, None, 'RS485 is disabled by default')
self.s.rs485_mode = serial.rs485.RS485Settings()
self.assertTrue(self.s._rs485_mode is not None, 'RS485 is enabled')
self.assertTrue(self.s.rs485_mode is not None, 'RS485 is enabled')
self.s.rs485_mode = None
self.assertEqual(self.s._rs485_mode, None, 'RS485 is disabled again')
self.assertEqual(self.s.rs485_mode, None, 'RS485 is disabled again')
class Test_RS485_class(unittest.TestCase):
"""Test RS485 class"""
def setUp(self):
if not isinstance(serial.serial_for_url(PORT), serial.Serial):
raise unittest.SkipTest("RS485 test only compatible with real serial port")
self.s = serial.rs485.RS485(PORT, timeout=1)
def tearDown(self):
self.s.close()
def test_RS485_class(self):
self.s.rs485_mode = serial.rs485.RS485Settings()
self.s.write(b'hello')
self.assertEqual(self.s.read(5), b'hello')
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,80 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2002-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Test the ability to get and set the settings with a dictionary.
Part of pySerial (http://pyserial.sf.net) (C) 2002-2015 cliechti@gmx.net
"""
import unittest
import serial
# on which port should the tests be performed:
PORT = 'loop://'
SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff',
'dsrdtr', 'rtscts', 'timeout', 'write_timeout', 'inter_byte_timeout')
class Test_SettingsDict(unittest.TestCase):
"""Test with settings dictionary"""
def test_getsettings(self):
"""the settings dict reflects the current settings"""
ser = serial.serial_for_url(PORT, do_not_open=True)
d = ser.get_settings()
for setting in SETTINGS:
self.assertEqual(getattr(ser, setting), d[setting])
def test_partial_settings(self):
"""partial settings dictionaries are also accepted"""
ser = serial.serial_for_url(PORT, do_not_open=True)
d = ser.get_settings()
del d['baudrate']
del d['bytesize']
ser.apply_settings(d)
for setting in d:
self.assertEqual(getattr(ser, setting), d[setting])
def test_unknown_settings(self):
"""unknown settings are ignored"""
ser = serial.serial_for_url(PORT, do_not_open=True)
d = ser.get_settings()
d['foobar'] = 'ignore me'
ser.apply_settings(d)
def test_init_sets_the_correct_attrs(self):
"""__init__ sets the fields that get_settings reads"""
for setting, value in (
('baudrate', 57600),
('timeout', 7),
('write_timeout', 12),
('inter_byte_timeout', 15),
('stopbits', serial.STOPBITS_TWO),
('bytesize', serial.SEVENBITS),
('parity', serial.PARITY_ODD),
('xonxoff', True),
('rtscts', True),
('dsrdtr', True)):
kwargs = {'do_not_open': True, setting: value}
ser = serial.serial_for_url(PORT, **kwargs)
d = ser.get_settings()
self.assertEqual(getattr(ser, setting), value)
self.assertEqual(d[setting], value)
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,75 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Test serial.threaded related functionality.
"""
import os
import unittest
import serial
import serial.threaded
import time
# on which port should the tests be performed:
PORT = 'loop://'
class Test_threaded(unittest.TestCase):
"""Test serial.threaded related functionality"""
def test_line_reader(self):
"""simple test of line reader class"""
class TestLines(serial.threaded.LineReader):
def __init__(self):
super(TestLines, self).__init__()
self.received_lines = []
def handle_line(self, data):
self.received_lines.append(data)
ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1)
with serial.threaded.ReaderThread(ser, TestLines) as protocol:
protocol.write_line('hello')
protocol.write_line('world')
time.sleep(1)
self.assertEqual(protocol.received_lines, ['hello', 'world'])
def test_framed_packet(self):
"""simple test of line reader class"""
class TestFramedPacket(serial.threaded.FramedPacket):
def __init__(self):
super(TestFramedPacket, self).__init__()
self.received_packets = []
def handle_packet(self, packet):
self.received_packets.append(packet)
def send_packet(self, packet):
self.transport.write(self.START)
self.transport.write(packet)
self.transport.write(self.STOP)
ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1)
with serial.threaded.ReaderThread(ser, TestFramedPacket) as protocol:
protocol.send_packet(b'1')
protocol.send_packet(b'2')
protocol.send_packet(b'3')
time.sleep(1)
self.assertEqual(protocol.received_packets, [b'1', b'2', b'3'])
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.stdout.write("Testing port: {!r}\n".format(PORT))
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,66 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""
Test Timeout helper class.
"""
import sys
import unittest
import time
from serial import serialutil
class TestTimeoutClass(unittest.TestCase):
"""Test the Timeout class"""
def test_simple_timeout(self):
"""Test simple timeout"""
t = serialutil.Timeout(2)
self.assertFalse(t.expired())
self.assertTrue(t.time_left() > 0)
time.sleep(2.1)
self.assertTrue(t.expired())
self.assertEqual(t.time_left(), 0)
def test_non_blocking(self):
"""Test nonblocking case (0)"""
t = serialutil.Timeout(0)
self.assertTrue(t.is_non_blocking)
self.assertFalse(t.is_infinite)
self.assertTrue(t.expired())
def test_blocking(self):
"""Test no timeout (None)"""
t = serialutil.Timeout(None)
self.assertFalse(t.is_non_blocking)
self.assertTrue(t.is_infinite)
#~ self.assertFalse(t.expired())
def test_changing_clock(self):
"""Test recovery from changing clock"""
class T(serialutil.Timeout):
def TIME(self):
return test_time
test_time = 1000
t = T(10)
self.assertEqual(t.target_time, 1010)
self.assertFalse(t.expired())
self.assertTrue(t.time_left() > 0)
test_time = 100 # clock jumps way back
self.assertTrue(t.time_left() > 0)
self.assertTrue(t.time_left() <= 10)
self.assertEqual(t.target_time, 110)
test_time = 10000 # jump way forward
self.assertEqual(t.time_left(), 0) # if will expire immediately
if __name__ == '__main__':
sys.stdout.write(__doc__)
if len(sys.argv) > 1:
PORT = sys.argv[1]
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,51 +0,0 @@
#! /usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Some tests for the serial module.
Part of pySerial (http://pyserial.sf.net) (C)2001-2011 cliechti@gmx.net
Intended to be run on different platforms, to ensure portability of
the code.
Cover some of the aspects of serial_for_url and the extension mechanism.
"""
import unittest
import serial
class Test_URL(unittest.TestCase):
"""Test serial_for_url"""
def test_loop(self):
"""loop interface"""
serial.serial_for_url('loop://', do_not_open=True)
def test_bad_url(self):
"""invalid protocol specified"""
self.assertRaises(ValueError, serial.serial_for_url, "imnotknown://")
def test_custom_url(self):
"""custom protocol handlers"""
# it's unknown
self.assertRaises(ValueError, serial.serial_for_url, "test://")
# add search path
serial.protocol_handler_packages.append('handlers')
# now it should work
serial.serial_for_url("test://")
# remove our handler again
serial.protocol_handler_packages.remove('handlers')
# so it should not work anymore
self.assertRaises(ValueError, serial.serial_for_url, "test://")
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
@@ -1,36 +0,0 @@
#!/usr/bin/env python
#
# This file is part of pySerial - Cross platform serial port support for Python
# (C) 2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Tests for utility functions of serualutil.
"""
import os
import unittest
import serial
class Test_util(unittest.TestCase):
"""Test serial utility functions"""
def test_to_bytes(self):
self.assertEqual(serial.to_bytes([1, 2, 3]), b'\x01\x02\x03')
self.assertEqual(serial.to_bytes(b'\x01\x02\x03'), b'\x01\x02\x03')
self.assertEqual(serial.to_bytes(bytearray([1,2,3])), b'\x01\x02\x03')
# unicode is not supported test. use decode() instead of u'' syntax to be
# compatible to Python 3.x < 3.4
self.assertRaises(TypeError, serial.to_bytes, b'hello'.decode('utf-8'))
def test_iterbytes(self):
self.assertEqual(list(serial.iterbytes(b'\x01\x02\x03')), [b'\x01', b'\x02', b'\x03'])
if __name__ == '__main__':
import sys
sys.stdout.write(__doc__)
sys.argv[1:] = ['-v']
# When this module is executed from the command-line, it runs all its tests
unittest.main()
-91
View File
@@ -1,91 +0,0 @@
#!/usr/bin/env python
#
# This is a wrapper module for different platform implementations
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2001-2020 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import absolute_import
import sys
import importlib
from serial.serialutil import *
#~ SerialBase, SerialException, to_bytes, iterbytes
__version__ = '3.5'
VERSION = __version__
# pylint: disable=wrong-import-position
if sys.platform == 'cli':
from serial.serialcli import Serial
else:
import os
# chose an implementation, depending on os
if os.name == 'nt': # sys.platform == 'win32':
from serial.serialwin32 import Serial
elif os.name == 'posix':
from serial.serialposix import Serial, PosixPollSerial, VTIMESerial # noqa
elif os.name == 'java':
from serial.serialjava import Serial
else:
raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name))
protocol_handler_packages = [
'serial.urlhandler',
]
def serial_for_url(url, *args, **kwargs):
"""\
Get an instance of the Serial class, depending on port/url. The port is not
opened when the keyword parameter 'do_not_open' is true, by default it
is. All other parameters are directly passed to the __init__ method when
the port is instantiated.
The list of package names that is searched for protocol handlers is kept in
``protocol_handler_packages``.
e.g. we want to support a URL ``foobar://``. A module
``my_handlers.protocol_foobar`` is provided by the user. Then
``protocol_handler_packages.append("my_handlers")`` would extend the search
path so that ``serial_for_url("foobar://"))`` would work.
"""
# check and remove extra parameter to not confuse the Serial class
do_open = not kwargs.pop('do_not_open', False)
# the default is to use the native implementation
klass = Serial
try:
url_lowercase = url.lower()
except AttributeError:
# it's not a string, use default
pass
else:
# if it is an URL, try to import the handler module from the list of possible packages
if '://' in url_lowercase:
protocol = url_lowercase.split('://', 1)[0]
module_name = '.protocol_{}'.format(protocol)
for package_name in protocol_handler_packages:
try:
importlib.import_module(package_name)
handler_module = importlib.import_module(module_name, package_name)
except ImportError:
continue
else:
if hasattr(handler_module, 'serial_class_for_url'):
url, klass = handler_module.serial_class_for_url(url)
else:
klass = handler_module.Serial
break
else:
raise ValueError('invalid URL, protocol {!r} not known'.format(protocol))
# instantiate and open when desired
instance = klass(None, *args, **kwargs)
instance.port = url
if do_open:
instance.open()
return instance
-3
View File
@@ -1,3 +0,0 @@
from .tools import miniterm
miniterm.main()
-1351
View File
File diff suppressed because it is too large Load Diff
-94
View File
@@ -1,94 +0,0 @@
#!/usr/bin/env python
# RS485 support
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
The settings for RS485 are stored in a dedicated object that can be applied to
serial ports (where supported).
NOTE: Some implementations may only support a subset of the settings.
"""
from __future__ import absolute_import
import time
import serial
class RS485Settings(object):
def __init__(
self,
rts_level_for_tx=True,
rts_level_for_rx=False,
loopback=False,
delay_before_tx=None,
delay_before_rx=None):
self.rts_level_for_tx = rts_level_for_tx
self.rts_level_for_rx = rts_level_for_rx
self.loopback = loopback
self.delay_before_tx = delay_before_tx
self.delay_before_rx = delay_before_rx
class RS485(serial.Serial):
"""\
A subclass that replaces the write method with one that toggles RTS
according to the RS485 settings.
NOTE: This may work unreliably on some serial ports (control signals not
synchronized or delayed compared to data). Using delays may be
unreliable (varying times, larger than expected) as the OS may not
support very fine grained delays (no smaller than in the order of
tens of milliseconds).
NOTE: Some implementations support this natively. Better performance
can be expected when the native version is used.
NOTE: The loopback property is ignored by this implementation. The actual
behavior depends on the used hardware.
Usage:
ser = RS485(...)
ser.rs485_mode = RS485Settings(...)
ser.write(b'hello')
"""
def __init__(self, *args, **kwargs):
super(RS485, self).__init__(*args, **kwargs)
self._alternate_rs485_settings = None
def write(self, b):
"""Write to port, controlling RTS before and after transmitting."""
if self._alternate_rs485_settings is not None:
# apply level for TX and optional delay
self.setRTS(self._alternate_rs485_settings.rts_level_for_tx)
if self._alternate_rs485_settings.delay_before_tx is not None:
time.sleep(self._alternate_rs485_settings.delay_before_tx)
# write and wait for data to be written
super(RS485, self).write(b)
super(RS485, self).flush()
# optional delay and apply level for RX
if self._alternate_rs485_settings.delay_before_rx is not None:
time.sleep(self._alternate_rs485_settings.delay_before_rx)
self.setRTS(self._alternate_rs485_settings.rts_level_for_rx)
else:
super(RS485, self).write(b)
# redirect where the property stores the settings so that underlying Serial
# instance does not see them
@property
def rs485_mode(self):
"""\
Enable RS485 mode and apply new settings, set to None to disable.
See serial.rs485.RS485Settings for more info about the value.
"""
return self._alternate_rs485_settings
@rs485_mode.setter
def rs485_mode(self, rs485_settings):
self._alternate_rs485_settings = rs485_settings
-253
View File
@@ -1,253 +0,0 @@
#! python
#
# Backend for .NET/Mono (IronPython), .NET >= 2
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2008-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import absolute_import
import System
import System.IO.Ports
from serial.serialutil import *
# must invoke function with byte array, make a helper to convert strings
# to byte arrays
sab = System.Array[System.Byte]
def as_byte_array(string):
return sab([ord(x) for x in string]) # XXX will require adaption when run with a 3.x compatible IronPython
class Serial(SerialBase):
"""Serial port implementation for .NET/Mono."""
BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200)
def open(self):
"""\
Open port with current settings. This may throw a SerialException
if the port cannot be opened.
"""
if self._port is None:
raise SerialException("Port must be configured before it can be used.")
if self.is_open:
raise SerialException("Port is already open.")
try:
self._port_handle = System.IO.Ports.SerialPort(self.portstr)
except Exception as msg:
self._port_handle = None
raise SerialException("could not open port %s: %s" % (self.portstr, msg))
# if RTS and/or DTR are not set before open, they default to True
if self._rts_state is None:
self._rts_state = True
if self._dtr_state is None:
self._dtr_state = True
self._reconfigure_port()
self._port_handle.Open()
self.is_open = True
if not self._dsrdtr:
self._update_dtr_state()
if not self._rtscts:
self._update_rts_state()
self.reset_input_buffer()
def _reconfigure_port(self):
"""Set communication parameters on opened port."""
if not self._port_handle:
raise SerialException("Can only operate on a valid port handle")
#~ self._port_handle.ReceivedBytesThreshold = 1
if self._timeout is None:
self._port_handle.ReadTimeout = System.IO.Ports.SerialPort.InfiniteTimeout
else:
self._port_handle.ReadTimeout = int(self._timeout * 1000)
# if self._timeout != 0 and self._interCharTimeout is not None:
# timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:]
if self._write_timeout is None:
self._port_handle.WriteTimeout = System.IO.Ports.SerialPort.InfiniteTimeout
else:
self._port_handle.WriteTimeout = int(self._write_timeout * 1000)
# Setup the connection info.
try:
self._port_handle.BaudRate = self._baudrate
except IOError as e:
# catch errors from illegal baudrate settings
raise ValueError(str(e))
if self._bytesize == FIVEBITS:
self._port_handle.DataBits = 5
elif self._bytesize == SIXBITS:
self._port_handle.DataBits = 6
elif self._bytesize == SEVENBITS:
self._port_handle.DataBits = 7
elif self._bytesize == EIGHTBITS:
self._port_handle.DataBits = 8
else:
raise ValueError("Unsupported number of data bits: %r" % self._bytesize)
if self._parity == PARITY_NONE:
self._port_handle.Parity = getattr(System.IO.Ports.Parity, 'None') # reserved keyword in Py3k
elif self._parity == PARITY_EVEN:
self._port_handle.Parity = System.IO.Ports.Parity.Even
elif self._parity == PARITY_ODD:
self._port_handle.Parity = System.IO.Ports.Parity.Odd
elif self._parity == PARITY_MARK:
self._port_handle.Parity = System.IO.Ports.Parity.Mark
elif self._parity == PARITY_SPACE:
self._port_handle.Parity = System.IO.Ports.Parity.Space
else:
raise ValueError("Unsupported parity mode: %r" % self._parity)
if self._stopbits == STOPBITS_ONE:
self._port_handle.StopBits = System.IO.Ports.StopBits.One
elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
self._port_handle.StopBits = System.IO.Ports.StopBits.OnePointFive
elif self._stopbits == STOPBITS_TWO:
self._port_handle.StopBits = System.IO.Ports.StopBits.Two
else:
raise ValueError("Unsupported number of stop bits: %r" % self._stopbits)
if self._rtscts and self._xonxoff:
self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSendXOnXOff
elif self._rtscts:
self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSend
elif self._xonxoff:
self._port_handle.Handshake = System.IO.Ports.Handshake.XOnXOff
else:
self._port_handle.Handshake = getattr(System.IO.Ports.Handshake, 'None') # reserved keyword in Py3k
#~ def __del__(self):
#~ self.close()
def close(self):
"""Close port"""
if self.is_open:
if self._port_handle:
try:
self._port_handle.Close()
except System.IO.Ports.InvalidOperationException:
# ignore errors. can happen for unplugged USB serial devices
pass
self._port_handle = None
self.is_open = False
# - - - - - - - - - - - - - - - - - - - - - - - -
@property
def in_waiting(self):
"""Return the number of characters currently in the input buffer."""
if not self.is_open:
raise PortNotOpenError()
return self._port_handle.BytesToRead
def read(self, size=1):
"""\
Read size bytes from the serial port. If a timeout is set it may
return less characters as requested. With no timeout it will block
until the requested number of bytes is read.
"""
if not self.is_open:
raise PortNotOpenError()
# must use single byte reads as this is the only way to read
# without applying encodings
data = bytearray()
while size:
try:
data.append(self._port_handle.ReadByte())
except System.TimeoutException:
break
else:
size -= 1
return bytes(data)
def write(self, data):
"""Output the given string over the serial port."""
if not self.is_open:
raise PortNotOpenError()
#~ if not isinstance(data, (bytes, bytearray)):
#~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
try:
# must call overloaded method with byte array argument
# as this is the only one not applying encodings
self._port_handle.Write(as_byte_array(data), 0, len(data))
except System.TimeoutException:
raise SerialTimeoutException('Write timeout')
return len(data)
def reset_input_buffer(self):
"""Clear input buffer, discarding all that is in the buffer."""
if not self.is_open:
raise PortNotOpenError()
self._port_handle.DiscardInBuffer()
def reset_output_buffer(self):
"""\
Clear output buffer, aborting the current output and
discarding all that is in the buffer.
"""
if not self.is_open:
raise PortNotOpenError()
self._port_handle.DiscardOutBuffer()
def _update_break_state(self):
"""
Set break: Controls TXD. When active, to transmitting is possible.
"""
if not self.is_open:
raise PortNotOpenError()
self._port_handle.BreakState = bool(self._break_state)
def _update_rts_state(self):
"""Set terminal status line: Request To Send"""
if not self.is_open:
raise PortNotOpenError()
self._port_handle.RtsEnable = bool(self._rts_state)
def _update_dtr_state(self):
"""Set terminal status line: Data Terminal Ready"""
if not self.is_open:
raise PortNotOpenError()
self._port_handle.DtrEnable = bool(self._dtr_state)
@property
def cts(self):
"""Read terminal status line: Clear To Send"""
if not self.is_open:
raise PortNotOpenError()
return self._port_handle.CtsHolding
@property
def dsr(self):
"""Read terminal status line: Data Set Ready"""
if not self.is_open:
raise PortNotOpenError()
return self._port_handle.DsrHolding
@property
def ri(self):
"""Read terminal status line: Ring Indicator"""
if not self.is_open:
raise PortNotOpenError()
#~ return self._port_handle.XXX
return False # XXX an error would be better
@property
def cd(self):
"""Read terminal status line: Carrier Detect"""
if not self.is_open:
raise PortNotOpenError()
return self._port_handle.CDHolding
# - - platform specific - - - -
# none
-251
View File
@@ -1,251 +0,0 @@
#!jython
#
# Backend Jython with JavaComm
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2002-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import absolute_import
from serial.serialutil import *
def my_import(name):
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
def detect_java_comm(names):
"""try given list of modules and return that imports"""
for name in names:
try:
mod = my_import(name)
mod.SerialPort
return mod
except (ImportError, AttributeError):
pass
raise ImportError("No Java Communications API implementation found")
# Java Communications API implementations
# http://mho.republika.pl/java/comm/
comm = detect_java_comm([
'javax.comm', # Sun/IBM
'gnu.io', # RXTX
])
def device(portnumber):
"""Turn a port number into a device name"""
enum = comm.CommPortIdentifier.getPortIdentifiers()
ports = []
while enum.hasMoreElements():
el = enum.nextElement()
if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL:
ports.append(el)
return ports[portnumber].getName()
class Serial(SerialBase):
"""\
Serial port class, implemented with Java Communications API and
thus usable with jython and the appropriate java extension.
"""
def open(self):
"""\
Open port with current settings. This may throw a SerialException
if the port cannot be opened.
"""
if self._port is None:
raise SerialException("Port must be configured before it can be used.")
if self.is_open:
raise SerialException("Port is already open.")
if type(self._port) == type(''): # strings are taken directly
portId = comm.CommPortIdentifier.getPortIdentifier(self._port)
else:
portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port)) # numbers are transformed to a comport id obj
try:
self.sPort = portId.open("python serial module", 10)
except Exception as msg:
self.sPort = None
raise SerialException("Could not open port: %s" % msg)
self._reconfigurePort()
self._instream = self.sPort.getInputStream()
self._outstream = self.sPort.getOutputStream()
self.is_open = True
def _reconfigurePort(self):
"""Set communication parameters on opened port."""
if not self.sPort:
raise SerialException("Can only operate on a valid port handle")
self.sPort.enableReceiveTimeout(30)
if self._bytesize == FIVEBITS:
jdatabits = comm.SerialPort.DATABITS_5
elif self._bytesize == SIXBITS:
jdatabits = comm.SerialPort.DATABITS_6
elif self._bytesize == SEVENBITS:
jdatabits = comm.SerialPort.DATABITS_7
elif self._bytesize == EIGHTBITS:
jdatabits = comm.SerialPort.DATABITS_8
else:
raise ValueError("unsupported bytesize: %r" % self._bytesize)
if self._stopbits == STOPBITS_ONE:
jstopbits = comm.SerialPort.STOPBITS_1
elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
jstopbits = comm.SerialPort.STOPBITS_1_5
elif self._stopbits == STOPBITS_TWO:
jstopbits = comm.SerialPort.STOPBITS_2
else:
raise ValueError("unsupported number of stopbits: %r" % self._stopbits)
if self._parity == PARITY_NONE:
jparity = comm.SerialPort.PARITY_NONE
elif self._parity == PARITY_EVEN:
jparity = comm.SerialPort.PARITY_EVEN
elif self._parity == PARITY_ODD:
jparity = comm.SerialPort.PARITY_ODD
elif self._parity == PARITY_MARK:
jparity = comm.SerialPort.PARITY_MARK
elif self._parity == PARITY_SPACE:
jparity = comm.SerialPort.PARITY_SPACE
else:
raise ValueError("unsupported parity type: %r" % self._parity)
jflowin = jflowout = 0
if self._rtscts:
jflowin |= comm.SerialPort.FLOWCONTROL_RTSCTS_IN
jflowout |= comm.SerialPort.FLOWCONTROL_RTSCTS_OUT
if self._xonxoff:
jflowin |= comm.SerialPort.FLOWCONTROL_XONXOFF_IN
jflowout |= comm.SerialPort.FLOWCONTROL_XONXOFF_OUT
self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity)
self.sPort.setFlowControlMode(jflowin | jflowout)
if self._timeout >= 0:
self.sPort.enableReceiveTimeout(int(self._timeout*1000))
else:
self.sPort.disableReceiveTimeout()
def close(self):
"""Close port"""
if self.is_open:
if self.sPort:
self._instream.close()
self._outstream.close()
self.sPort.close()
self.sPort = None
self.is_open = False
# - - - - - - - - - - - - - - - - - - - - - - - -
@property
def in_waiting(self):
"""Return the number of characters currently in the input buffer."""
if not self.sPort:
raise PortNotOpenError()
return self._instream.available()
def read(self, size=1):
"""\
Read size bytes from the serial port. If a timeout is set it may
return less characters as requested. With no timeout it will block
until the requested number of bytes is read.
"""
if not self.sPort:
raise PortNotOpenError()
read = bytearray()
if size > 0:
while len(read) < size:
x = self._instream.read()
if x == -1:
if self.timeout >= 0:
break
else:
read.append(x)
return bytes(read)
def write(self, data):
"""Output the given string over the serial port."""
if not self.sPort:
raise PortNotOpenError()
if not isinstance(data, (bytes, bytearray)):
raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
self._outstream.write(data)
return len(data)
def reset_input_buffer(self):
"""Clear input buffer, discarding all that is in the buffer."""
if not self.sPort:
raise PortNotOpenError()
self._instream.skip(self._instream.available())
def reset_output_buffer(self):
"""\
Clear output buffer, aborting the current output and
discarding all that is in the buffer.
"""
if not self.sPort:
raise PortNotOpenError()
self._outstream.flush()
def send_break(self, duration=0.25):
"""Send break condition. Timed, returns to idle state after given duration."""
if not self.sPort:
raise PortNotOpenError()
self.sPort.sendBreak(duration*1000.0)
def _update_break_state(self):
"""Set break: Controls TXD. When active, to transmitting is possible."""
if self.fd is None:
raise PortNotOpenError()
raise SerialException("The _update_break_state function is not implemented in java.")
def _update_rts_state(self):
"""Set terminal status line: Request To Send"""
if not self.sPort:
raise PortNotOpenError()
self.sPort.setRTS(self._rts_state)
def _update_dtr_state(self):
"""Set terminal status line: Data Terminal Ready"""
if not self.sPort:
raise PortNotOpenError()
self.sPort.setDTR(self._dtr_state)
@property
def cts(self):
"""Read terminal status line: Clear To Send"""
if not self.sPort:
raise PortNotOpenError()
self.sPort.isCTS()
@property
def dsr(self):
"""Read terminal status line: Data Set Ready"""
if not self.sPort:
raise PortNotOpenError()
self.sPort.isDSR()
@property
def ri(self):
"""Read terminal status line: Ring Indicator"""
if not self.sPort:
raise PortNotOpenError()
self.sPort.isRI()
@property
def cd(self):
"""Read terminal status line: Carrier Detect"""
if not self.sPort:
raise PortNotOpenError()
self.sPort.isCD()
-907
View File
@@ -1,907 +0,0 @@
#!/usr/bin/env python
#
# backend for serial IO for POSIX compatible systems, like Linux, OSX
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2001-2020 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
#
# parts based on code from Grant B. Edwards <grante@visi.com>:
# ftp://ftp.visi.com/users/grante/python/PosixSerial.py
#
# references: http://www.easysw.com/~mike/serial/serial.html
# Collection of port names (was previously used by number_to_device which was
# removed.
# - Linux /dev/ttyS%d (confirmed)
# - cygwin/win32 /dev/com%d (confirmed)
# - openbsd (OpenBSD) /dev/cua%02d
# - bsd*, freebsd* /dev/cuad%d
# - darwin (OS X) /dev/cuad%d
# - netbsd /dev/dty%02d (NetBSD 1.6 testing by Erk)
# - irix (IRIX) /dev/ttyf%d (partially tested) names depending on flow control
# - hp (HP-UX) /dev/tty%dp0 (not tested)
# - sunos (Solaris/SunOS) /dev/tty%c (letters, 'a'..'z') (confirmed)
# - aix (AIX) /dev/tty%d
from __future__ import absolute_import
# pylint: disable=abstract-method
import errno
import fcntl
import os
import platform
import select
import struct
import sys
import termios
import serial
from serial.serialutil import SerialBase, SerialException, to_bytes, \
PortNotOpenError, SerialTimeoutException, Timeout
class PlatformSpecificBase(object):
BAUDRATE_CONSTANTS = {}
def _set_special_baudrate(self, baudrate):
raise NotImplementedError('non-standard baudrates are not supported on this platform')
def _set_rs485_mode(self, rs485_settings):
raise NotImplementedError('RS485 not supported on this platform')
def set_low_latency_mode(self, low_latency_settings):
raise NotImplementedError('Low latency not supported on this platform')
def _update_break_state(self):
"""\
Set break: Controls TXD. When active, no transmitting is possible.
"""
if self._break_state:
fcntl.ioctl(self.fd, TIOCSBRK)
else:
fcntl.ioctl(self.fd, TIOCCBRK)
# some systems support an extra flag to enable the two in POSIX unsupported
# paritiy settings for MARK and SPACE
CMSPAR = 0 # default, for unsupported platforms, override below
# try to detect the OS so that a device can be selected...
# this code block should supply a device() and set_special_baudrate() function
# for the platform
plat = sys.platform.lower()
if plat[:5] == 'linux': # Linux (confirmed) # noqa
import array
# extra termios flags
CMSPAR = 0o10000000000 # Use "stick" (mark/space) parity
# baudrate ioctls
if platform.machine().lower() == "mips":
TCGETS2 = 0x4030542A
TCSETS2 = 0x8030542B
BAUDRATE_OFFSET = 10
else:
TCGETS2 = 0x802C542A
TCSETS2 = 0x402C542B
BAUDRATE_OFFSET = 9
BOTHER = 0o010000
# RS485 ioctls
TIOCGRS485 = 0x542E
TIOCSRS485 = 0x542F
SER_RS485_ENABLED = 0b00000001
SER_RS485_RTS_ON_SEND = 0b00000010
SER_RS485_RTS_AFTER_SEND = 0b00000100
SER_RS485_RX_DURING_TX = 0b00010000
class PlatformSpecific(PlatformSpecificBase):
BAUDRATE_CONSTANTS = {
0: 0o000000, # hang up
50: 0o000001,
75: 0o000002,
110: 0o000003,
134: 0o000004,
150: 0o000005,
200: 0o000006,
300: 0o000007,
600: 0o000010,
1200: 0o000011,
1800: 0o000012,
2400: 0o000013,
4800: 0o000014,
9600: 0o000015,
19200: 0o000016,
38400: 0o000017,
57600: 0o010001,
115200: 0o010002,
230400: 0o010003,
460800: 0o010004,
500000: 0o010005,
576000: 0o010006,
921600: 0o010007,
1000000: 0o010010,
1152000: 0o010011,
1500000: 0o010012,
2000000: 0o010013,
2500000: 0o010014,
3000000: 0o010015,
3500000: 0o010016,
4000000: 0o010017
}
def set_low_latency_mode(self, low_latency_settings):
buf = array.array('i', [0] * 32)
try:
# get serial_struct
fcntl.ioctl(self.fd, termios.TIOCGSERIAL, buf)
# set or unset ASYNC_LOW_LATENCY flag
if low_latency_settings:
buf[4] |= 0x2000
else:
buf[4] &= ~0x2000
# set serial_struct
fcntl.ioctl(self.fd, termios.TIOCSSERIAL, buf)
except IOError as e:
raise ValueError('Failed to update ASYNC_LOW_LATENCY flag to {}: {}'.format(low_latency_settings, e))
def _set_special_baudrate(self, baudrate):
# right size is 44 on x86_64, allow for some growth
buf = array.array('i', [0] * 64)
try:
# get serial_struct
fcntl.ioctl(self.fd, TCGETS2, buf)
# set custom speed
buf[2] &= ~termios.CBAUD
buf[2] |= BOTHER
buf[BAUDRATE_OFFSET] = buf[BAUDRATE_OFFSET + 1] = baudrate
# set serial_struct
fcntl.ioctl(self.fd, TCSETS2, buf)
except IOError as e:
raise ValueError('Failed to set custom baud rate ({}): {}'.format(baudrate, e))
def _set_rs485_mode(self, rs485_settings):
buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding
try:
fcntl.ioctl(self.fd, TIOCGRS485, buf)
buf[0] |= SER_RS485_ENABLED
if rs485_settings is not None:
if rs485_settings.loopback:
buf[0] |= SER_RS485_RX_DURING_TX
else:
buf[0] &= ~SER_RS485_RX_DURING_TX
if rs485_settings.rts_level_for_tx:
buf[0] |= SER_RS485_RTS_ON_SEND
else:
buf[0] &= ~SER_RS485_RTS_ON_SEND
if rs485_settings.rts_level_for_rx:
buf[0] |= SER_RS485_RTS_AFTER_SEND
else:
buf[0] &= ~SER_RS485_RTS_AFTER_SEND
if rs485_settings.delay_before_tx is not None:
buf[1] = int(rs485_settings.delay_before_tx * 1000)
if rs485_settings.delay_before_rx is not None:
buf[2] = int(rs485_settings.delay_before_rx * 1000)
else:
buf[0] = 0 # clear SER_RS485_ENABLED
fcntl.ioctl(self.fd, TIOCSRS485, buf)
except IOError as e:
raise ValueError('Failed to set RS485 mode: {}'.format(e))
elif plat == 'cygwin': # cygwin/win32 (confirmed)
class PlatformSpecific(PlatformSpecificBase):
BAUDRATE_CONSTANTS = {
128000: 0x01003,
256000: 0x01005,
500000: 0x01007,
576000: 0x01008,
921600: 0x01009,
1000000: 0x0100a,
1152000: 0x0100b,
1500000: 0x0100c,
2000000: 0x0100d,
2500000: 0x0100e,
3000000: 0x0100f
}
elif plat[:6] == 'darwin': # OS X
import array
IOSSIOSPEED = 0x80045402 # _IOW('T', 2, speed_t)
class PlatformSpecific(PlatformSpecificBase):
osx_version = os.uname()[2].split('.')
TIOCSBRK = 0x2000747B # _IO('t', 123)
TIOCCBRK = 0x2000747A # _IO('t', 122)
# Tiger or above can support arbitrary serial speeds
if int(osx_version[0]) >= 8:
def _set_special_baudrate(self, baudrate):
# use IOKit-specific call to set up high speeds
buf = array.array('i', [baudrate])
fcntl.ioctl(self.fd, IOSSIOSPEED, buf, 1)
def _update_break_state(self):
"""\
Set break: Controls TXD. When active, no transmitting is possible.
"""
if self._break_state:
fcntl.ioctl(self.fd, PlatformSpecific.TIOCSBRK)
else:
fcntl.ioctl(self.fd, PlatformSpecific.TIOCCBRK)
elif plat[:3] == 'bsd' or \
plat[:7] == 'freebsd' or \
plat[:6] == 'netbsd' or \
plat[:7] == 'openbsd':
class ReturnBaudrate(object):
def __getitem__(self, key):
return key
class PlatformSpecific(PlatformSpecificBase):
# Only tested on FreeBSD:
# The baud rate may be passed in as
# a literal value.
BAUDRATE_CONSTANTS = ReturnBaudrate()
TIOCSBRK = 0x2000747B # _IO('t', 123)
TIOCCBRK = 0x2000747A # _IO('t', 122)
def _update_break_state(self):
"""\
Set break: Controls TXD. When active, no transmitting is possible.
"""
if self._break_state:
fcntl.ioctl(self.fd, PlatformSpecific.TIOCSBRK)
else:
fcntl.ioctl(self.fd, PlatformSpecific.TIOCCBRK)
else:
class PlatformSpecific(PlatformSpecificBase):
pass
# load some constants for later use.
# try to use values from termios, use defaults from linux otherwise
TIOCMGET = getattr(termios, 'TIOCMGET', 0x5415)
TIOCMBIS = getattr(termios, 'TIOCMBIS', 0x5416)
TIOCMBIC = getattr(termios, 'TIOCMBIC', 0x5417)
TIOCMSET = getattr(termios, 'TIOCMSET', 0x5418)
# TIOCM_LE = getattr(termios, 'TIOCM_LE', 0x001)
TIOCM_DTR = getattr(termios, 'TIOCM_DTR', 0x002)
TIOCM_RTS = getattr(termios, 'TIOCM_RTS', 0x004)
# TIOCM_ST = getattr(termios, 'TIOCM_ST', 0x008)
# TIOCM_SR = getattr(termios, 'TIOCM_SR', 0x010)
TIOCM_CTS = getattr(termios, 'TIOCM_CTS', 0x020)
TIOCM_CAR = getattr(termios, 'TIOCM_CAR', 0x040)
TIOCM_RNG = getattr(termios, 'TIOCM_RNG', 0x080)
TIOCM_DSR = getattr(termios, 'TIOCM_DSR', 0x100)
TIOCM_CD = getattr(termios, 'TIOCM_CD', TIOCM_CAR)
TIOCM_RI = getattr(termios, 'TIOCM_RI', TIOCM_RNG)
# TIOCM_OUT1 = getattr(termios, 'TIOCM_OUT1', 0x2000)
# TIOCM_OUT2 = getattr(termios, 'TIOCM_OUT2', 0x4000)
if hasattr(termios, 'TIOCINQ'):
TIOCINQ = termios.TIOCINQ
else:
TIOCINQ = getattr(termios, 'FIONREAD', 0x541B)
TIOCOUTQ = getattr(termios, 'TIOCOUTQ', 0x5411)
TIOCM_zero_str = struct.pack('I', 0)
TIOCM_RTS_str = struct.pack('I', TIOCM_RTS)
TIOCM_DTR_str = struct.pack('I', TIOCM_DTR)
TIOCSBRK = getattr(termios, 'TIOCSBRK', 0x5427)
TIOCCBRK = getattr(termios, 'TIOCCBRK', 0x5428)
class Serial(SerialBase, PlatformSpecific):
"""\
Serial port class POSIX implementation. Serial port configuration is
done with termios and fcntl. Runs on Linux and many other Un*x like
systems.
"""
def open(self):
"""\
Open port with current settings. This may throw a SerialException
if the port cannot be opened."""
if self._port is None:
raise SerialException("Port must be configured before it can be used.")
if self.is_open:
raise SerialException("Port is already open.")
self.fd = None
# open
try:
self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)
except OSError as msg:
self.fd = None
raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg))
#~ fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # set blocking
self.pipe_abort_read_r, self.pipe_abort_read_w = None, None
self.pipe_abort_write_r, self.pipe_abort_write_w = None, None
try:
self._reconfigure_port(force_update=True)
try:
if not self._dsrdtr:
self._update_dtr_state()
if not self._rtscts:
self._update_rts_state()
except IOError as e:
# ignore Invalid argument and Inappropriate ioctl
if e.errno not in (errno.EINVAL, errno.ENOTTY):
raise
self._reset_input_buffer()
self.pipe_abort_read_r, self.pipe_abort_read_w = os.pipe()
self.pipe_abort_write_r, self.pipe_abort_write_w = os.pipe()
fcntl.fcntl(self.pipe_abort_read_r, fcntl.F_SETFL, os.O_NONBLOCK)
fcntl.fcntl(self.pipe_abort_write_r, fcntl.F_SETFL, os.O_NONBLOCK)
except BaseException:
try:
os.close(self.fd)
except Exception:
# ignore any exception when closing the port
# also to keep original exception that happened when setting up
pass
self.fd = None
if self.pipe_abort_read_w is not None:
os.close(self.pipe_abort_read_w)
self.pipe_abort_read_w = None
if self.pipe_abort_read_r is not None:
os.close(self.pipe_abort_read_r)
self.pipe_abort_read_r = None
if self.pipe_abort_write_w is not None:
os.close(self.pipe_abort_write_w)
self.pipe_abort_write_w = None
if self.pipe_abort_write_r is not None:
os.close(self.pipe_abort_write_r)
self.pipe_abort_write_r = None
raise
self.is_open = True
def _reconfigure_port(self, force_update=False):
"""Set communication parameters on opened port."""
if self.fd is None:
raise SerialException("Can only operate on a valid file descriptor")
# if exclusive lock is requested, create it before we modify anything else
if self._exclusive is not None:
if self._exclusive:
try:
fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError as msg:
raise SerialException(msg.errno, "Could not exclusively lock port {}: {}".format(self._port, msg))
else:
fcntl.flock(self.fd, fcntl.LOCK_UN)
custom_baud = None
vmin = vtime = 0 # timeout is done via select
if self._inter_byte_timeout is not None:
vmin = 1
vtime = int(self._inter_byte_timeout * 10)
try:
orig_attr = termios.tcgetattr(self.fd)
iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr
except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here
raise SerialException("Could not configure port: {}".format(msg))
# set up raw mode / no echo / binary
cflag |= (termios.CLOCAL | termios.CREAD)
lflag &= ~(termios.ICANON | termios.ECHO | termios.ECHOE |
termios.ECHOK | termios.ECHONL |
termios.ISIG | termios.IEXTEN) # |termios.ECHOPRT
for flag in ('ECHOCTL', 'ECHOKE'): # netbsd workaround for Erk
if hasattr(termios, flag):
lflag &= ~getattr(termios, flag)
oflag &= ~(termios.OPOST | termios.ONLCR | termios.OCRNL)
iflag &= ~(termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IGNBRK)
if hasattr(termios, 'IUCLC'):
iflag &= ~termios.IUCLC
if hasattr(termios, 'PARMRK'):
iflag &= ~termios.PARMRK
# setup baud rate
try:
ispeed = ospeed = getattr(termios, 'B{}'.format(self._baudrate))
except AttributeError:
try:
ispeed = ospeed = self.BAUDRATE_CONSTANTS[self._baudrate]
except KeyError:
#~ raise ValueError('Invalid baud rate: %r' % self._baudrate)
# See if BOTHER is defined for this platform; if it is, use
# this for a speed not defined in the baudrate constants list.
try:
ispeed = ospeed = BOTHER
except NameError:
# may need custom baud rate, it isn't in our list.
ispeed = ospeed = getattr(termios, 'B38400')
try:
custom_baud = int(self._baudrate) # store for later
except ValueError:
raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate))
else:
if custom_baud < 0:
raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate))
# setup char len
cflag &= ~termios.CSIZE
if self._bytesize == 8:
cflag |= termios.CS8
elif self._bytesize == 7:
cflag |= termios.CS7
elif self._bytesize == 6:
cflag |= termios.CS6
elif self._bytesize == 5:
cflag |= termios.CS5
else:
raise ValueError('Invalid char len: {!r}'.format(self._bytesize))
# setup stop bits
if self._stopbits == serial.STOPBITS_ONE:
cflag &= ~(termios.CSTOPB)
elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE:
cflag |= (termios.CSTOPB) # XXX same as TWO.. there is no POSIX support for 1.5
elif self._stopbits == serial.STOPBITS_TWO:
cflag |= (termios.CSTOPB)
else:
raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits))
# setup parity
iflag &= ~(termios.INPCK | termios.ISTRIP)
if self._parity == serial.PARITY_NONE:
cflag &= ~(termios.PARENB | termios.PARODD | CMSPAR)
elif self._parity == serial.PARITY_EVEN:
cflag &= ~(termios.PARODD | CMSPAR)
cflag |= (termios.PARENB)
elif self._parity == serial.PARITY_ODD:
cflag &= ~CMSPAR
cflag |= (termios.PARENB | termios.PARODD)
elif self._parity == serial.PARITY_MARK and CMSPAR:
cflag |= (termios.PARENB | CMSPAR | termios.PARODD)
elif self._parity == serial.PARITY_SPACE and CMSPAR:
cflag |= (termios.PARENB | CMSPAR)
cflag &= ~(termios.PARODD)
else:
raise ValueError('Invalid parity: {!r}'.format(self._parity))
# setup flow control
# xonxoff
if hasattr(termios, 'IXANY'):
if self._xonxoff:
iflag |= (termios.IXON | termios.IXOFF) # |termios.IXANY)
else:
iflag &= ~(termios.IXON | termios.IXOFF | termios.IXANY)
else:
if self._xonxoff:
iflag |= (termios.IXON | termios.IXOFF)
else:
iflag &= ~(termios.IXON | termios.IXOFF)
# rtscts
if hasattr(termios, 'CRTSCTS'):
if self._rtscts:
cflag |= (termios.CRTSCTS)
else:
cflag &= ~(termios.CRTSCTS)
elif hasattr(termios, 'CNEW_RTSCTS'): # try it with alternate constant name
if self._rtscts:
cflag |= (termios.CNEW_RTSCTS)
else:
cflag &= ~(termios.CNEW_RTSCTS)
# XXX should there be a warning if setting up rtscts (and xonxoff etc) fails??
# buffer
# vmin "minimal number of characters to be read. 0 for non blocking"
if vmin < 0 or vmin > 255:
raise ValueError('Invalid vmin: {!r}'.format(vmin))
cc[termios.VMIN] = vmin
# vtime
if vtime < 0 or vtime > 255:
raise ValueError('Invalid vtime: {!r}'.format(vtime))
cc[termios.VTIME] = vtime
# activate settings
if force_update or [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr:
termios.tcsetattr(
self.fd,
termios.TCSANOW,
[iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
# apply custom baud rate, if any
if custom_baud is not None:
self._set_special_baudrate(custom_baud)
if self._rs485_mode is not None:
self._set_rs485_mode(self._rs485_mode)
def close(self):
"""Close port"""
if self.is_open:
if self.fd is not None:
os.close(self.fd)
self.fd = None
os.close(self.pipe_abort_read_w)
os.close(self.pipe_abort_read_r)
os.close(self.pipe_abort_write_w)
os.close(self.pipe_abort_write_r)
self.pipe_abort_read_r, self.pipe_abort_read_w = None, None
self.pipe_abort_write_r, self.pipe_abort_write_w = None, None
self.is_open = False
# - - - - - - - - - - - - - - - - - - - - - - - -
@property
def in_waiting(self):
"""Return the number of bytes currently in the input buffer."""
#~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str)
s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str)
return struct.unpack('I', s)[0]
# select based implementation, proved to work on many systems
def read(self, size=1):
"""\
Read size bytes from the serial port. If a timeout is set it may
return less characters as requested. With no timeout it will block
until the requested number of bytes is read.
"""
if not self.is_open:
raise PortNotOpenError()
read = bytearray()
timeout = Timeout(self._timeout)
while len(read) < size:
try:
ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout.time_left())
if self.pipe_abort_read_r in ready:
os.read(self.pipe_abort_read_r, 1000)
break
# If select was used with a timeout, and the timeout occurs, it
# returns with empty lists -> thus abort read operation.
# For timeout == 0 (non-blocking operation) also abort when
# there is nothing to read.
if not ready:
break # timeout
buf = os.read(self.fd, size - len(read))
except OSError as e:
# this is for Python 3.x where select.error is a subclass of
# OSError ignore BlockingIOErrors and EINTR. other errors are shown
# https://www.python.org/dev/peps/pep-0475.
if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('read failed: {}'.format(e))
except select.error as e:
# this is for Python 2.x
# ignore BlockingIOErrors and EINTR. all errors are shown
# see also http://www.python.org/dev/peps/pep-3151/#select
if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('read failed: {}'.format(e))
else:
# read should always return some data as select reported it was
# ready to read when we get to this point.
if not buf:
# Disconnected devices, at least on Linux, show the
# behavior that they are always ready to read immediately
# but reading returns nothing.
raise SerialException(
'device reports readiness to read but returned no data '
'(device disconnected or multiple access on port?)')
read.extend(buf)
if timeout.expired():
break
return bytes(read)
def cancel_read(self):
if self.is_open:
os.write(self.pipe_abort_read_w, b"x")
def cancel_write(self):
if self.is_open:
os.write(self.pipe_abort_write_w, b"x")
def write(self, data):
"""Output the given byte string over the serial port."""
if not self.is_open:
raise PortNotOpenError()
d = to_bytes(data)
tx_len = length = len(d)
timeout = Timeout(self._write_timeout)
while tx_len > 0:
try:
n = os.write(self.fd, d)
if timeout.is_non_blocking:
# Zero timeout indicates non-blocking - simply return the
# number of bytes of data actually written
return n
elif not timeout.is_infinite:
# when timeout is set, use select to wait for being ready
# with the time left as timeout
if timeout.expired():
raise SerialTimeoutException('Write timeout')
abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], timeout.time_left())
if abort:
os.read(self.pipe_abort_write_r, 1000)
break
if not ready:
raise SerialTimeoutException('Write timeout')
else:
assert timeout.time_left() is None
# wait for write operation
abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], None)
if abort:
os.read(self.pipe_abort_write_r, 1)
break
if not ready:
raise SerialException('write failed (select)')
d = d[n:]
tx_len -= n
except SerialException:
raise
except OSError as e:
# this is for Python 3.x where select.error is a subclass of
# OSError ignore BlockingIOErrors and EINTR. other errors are shown
# https://www.python.org/dev/peps/pep-0475.
if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('write failed: {}'.format(e))
except select.error as e:
# this is for Python 2.x
# ignore BlockingIOErrors and EINTR. all errors are shown
# see also http://www.python.org/dev/peps/pep-3151/#select
if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('write failed: {}'.format(e))
if not timeout.is_non_blocking and timeout.expired():
raise SerialTimeoutException('Write timeout')
return length - len(d)
def flush(self):
"""\
Flush of file like objects. In this case, wait until all data
is written.
"""
if not self.is_open:
raise PortNotOpenError()
termios.tcdrain(self.fd)
def _reset_input_buffer(self):
"""Clear input buffer, discarding all that is in the buffer."""
termios.tcflush(self.fd, termios.TCIFLUSH)
def reset_input_buffer(self):
"""Clear input buffer, discarding all that is in the buffer."""
if not self.is_open:
raise PortNotOpenError()
self._reset_input_buffer()
def reset_output_buffer(self):
"""\
Clear output buffer, aborting the current output and discarding all
that is in the buffer.
"""
if not self.is_open:
raise PortNotOpenError()
termios.tcflush(self.fd, termios.TCOFLUSH)
def send_break(self, duration=0.25):
"""\
Send break condition. Timed, returns to idle state after given
duration.
"""
if not self.is_open:
raise PortNotOpenError()
termios.tcsendbreak(self.fd, int(duration / 0.25))
def _update_rts_state(self):
"""Set terminal status line: Request To Send"""
if self._rts_state:
fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str)
else:
fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str)
def _update_dtr_state(self):
"""Set terminal status line: Data Terminal Ready"""
if self._dtr_state:
fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str)
else:
fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str)
@property
def cts(self):
"""Read terminal status line: Clear To Send"""
if not self.is_open:
raise PortNotOpenError()
s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
return struct.unpack('I', s)[0] & TIOCM_CTS != 0
@property
def dsr(self):
"""Read terminal status line: Data Set Ready"""
if not self.is_open:
raise PortNotOpenError()
s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
return struct.unpack('I', s)[0] & TIOCM_DSR != 0
@property
def ri(self):
"""Read terminal status line: Ring Indicator"""
if not self.is_open:
raise PortNotOpenError()
s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
return struct.unpack('I', s)[0] & TIOCM_RI != 0
@property
def cd(self):
"""Read terminal status line: Carrier Detect"""
if not self.is_open:
raise PortNotOpenError()
s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
return struct.unpack('I', s)[0] & TIOCM_CD != 0
# - - platform specific - - - -
@property
def out_waiting(self):
"""Return the number of bytes currently in the output buffer."""
#~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str)
s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str)
return struct.unpack('I', s)[0]
def fileno(self):
"""\
For easier use of the serial port instance with select.
WARNING: this function is not portable to different platforms!
"""
if not self.is_open:
raise PortNotOpenError()
return self.fd
def set_input_flow_control(self, enable=True):
"""\
Manually control flow - when software flow control is enabled.
This will send XON (true) or XOFF (false) to the other device.
WARNING: this function is not portable to different platforms!
"""
if not self.is_open:
raise PortNotOpenError()
if enable:
termios.tcflow(self.fd, termios.TCION)
else:
termios.tcflow(self.fd, termios.TCIOFF)
def set_output_flow_control(self, enable=True):
"""\
Manually control flow of outgoing data - when hardware or software flow
control is enabled.
WARNING: this function is not portable to different platforms!
"""
if not self.is_open:
raise PortNotOpenError()
if enable:
termios.tcflow(self.fd, termios.TCOON)
else:
termios.tcflow(self.fd, termios.TCOOFF)
def nonblocking(self):
"""DEPRECATED - has no use"""
import warnings
warnings.warn("nonblocking() has no effect, already nonblocking", DeprecationWarning)
class PosixPollSerial(Serial):
"""\
Poll based read implementation. Not all systems support poll properly.
However this one has better handling of errors, such as a device
disconnecting while it's in use (e.g. USB-serial unplugged).
"""
def read(self, size=1):
"""\
Read size bytes from the serial port. If a timeout is set it may
return less characters as requested. With no timeout it will block
until the requested number of bytes is read.
"""
if not self.is_open:
raise PortNotOpenError()
read = bytearray()
timeout = Timeout(self._timeout)
poll = select.poll()
poll.register(self.fd, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL)
poll.register(self.pipe_abort_read_r, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL)
if size > 0:
while len(read) < size:
# print "\tread(): size",size, "have", len(read) #debug
# wait until device becomes ready to read (or something fails)
for fd, event in poll.poll(None if timeout.is_infinite else (timeout.time_left() * 1000)):
if fd == self.pipe_abort_read_r:
break
if event & (select.POLLERR | select.POLLHUP | select.POLLNVAL):
raise SerialException('device reports error (poll)')
# we don't care if it is select.POLLIN or timeout, that's
# handled below
if fd == self.pipe_abort_read_r:
os.read(self.pipe_abort_read_r, 1000)
break
buf = os.read(self.fd, size - len(read))
read.extend(buf)
if timeout.expired() \
or (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0) and not buf:
break # early abort on timeout
return bytes(read)
class VTIMESerial(Serial):
"""\
Implement timeout using vtime of tty device instead of using select.
This means that no inter character timeout can be specified and that
the error handling is degraded.
Overall timeout is disabled when inter-character timeout is used.
Note that this implementation does NOT support cancel_read(), it will
just ignore that.
"""
def _reconfigure_port(self, force_update=True):
"""Set communication parameters on opened port."""
super(VTIMESerial, self)._reconfigure_port()
fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # clear O_NONBLOCK
if self._inter_byte_timeout is not None:
vmin = 1
vtime = int(self._inter_byte_timeout * 10)
elif self._timeout is None:
vmin = 1
vtime = 0
else:
vmin = 0
vtime = int(self._timeout * 10)
try:
orig_attr = termios.tcgetattr(self.fd)
iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr
except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here
raise serial.SerialException("Could not configure port: {}".format(msg))
if vtime < 0 or vtime > 255:
raise ValueError('Invalid vtime: {!r}'.format(vtime))
cc[termios.VTIME] = vtime
cc[termios.VMIN] = vmin
termios.tcsetattr(
self.fd,
termios.TCSANOW,
[iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
def read(self, size=1):
"""\
Read size bytes from the serial port. If a timeout is set it may
return less characters as requested. With no timeout it will block
until the requested number of bytes is read.
"""
if not self.is_open:
raise PortNotOpenError()
read = bytearray()
while len(read) < size:
buf = os.read(self.fd, size - len(read))
if not buf:
break
read.extend(buf)
return bytes(read)
# hack to make hasattr return false
cancel_read = property()
-707
View File
@@ -1,707 +0,0 @@
#! python
#
# Base class and support functions used by various backends.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2001-2020 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import absolute_import
import io
import time
# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)``
# isn't returning the contents (very unfortunate). Therefore we need special
# cases and test for it. Ensure that there is a ``memoryview`` object for older
# Python versions. This is easier than making every test dependent on its
# existence.
try:
memoryview
except (NameError, AttributeError):
# implementation does not matter as we do not really use it.
# it just must not inherit from something else we might care for.
class memoryview(object): # pylint: disable=redefined-builtin,invalid-name
pass
try:
unicode
except (NameError, AttributeError):
unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name
try:
basestring
except (NameError, AttributeError):
basestring = (str,) # for Python 3, pylint: disable=redefined-builtin,invalid-name
# "for byte in data" fails for python3 as it returns ints instead of bytes
def iterbytes(b):
"""Iterate over bytes, returning bytes instead of ints (python3)"""
if isinstance(b, memoryview):
b = b.tobytes()
i = 0
while True:
a = b[i:i + 1]
i += 1
if a:
yield a
else:
break
# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11'
# so a simple ``bytes(sequence)`` doesn't work for all versions
def to_bytes(seq):
"""convert a sequence to a bytes type"""
if isinstance(seq, bytes):
return seq
elif isinstance(seq, bytearray):
return bytes(seq)
elif isinstance(seq, memoryview):
return seq.tobytes()
elif isinstance(seq, unicode):
raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq))
else:
# handle list of integers and bytes (one or more items) for Python 2 and 3
return bytes(bytearray(seq))
# create control bytes
XON = to_bytes([17])
XOFF = to_bytes([19])
CR = to_bytes([13])
LF = to_bytes([10])
PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S'
STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2)
FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8)
PARITY_NAMES = {
PARITY_NONE: 'None',
PARITY_EVEN: 'Even',
PARITY_ODD: 'Odd',
PARITY_MARK: 'Mark',
PARITY_SPACE: 'Space',
}
class SerialException(IOError):
"""Base class for serial port related exceptions."""
class SerialTimeoutException(SerialException):
"""Write timeouts give an exception"""
class PortNotOpenError(SerialException):
"""Port is not open"""
def __init__(self):
super(PortNotOpenError, self).__init__('Attempting to use a port that is not open')
class Timeout(object):
"""\
Abstraction for timeout operations. Using time.monotonic() if available
or time.time() in all other cases.
The class can also be initialized with 0 or None, in order to support
non-blocking and fully blocking I/O operations. The attributes
is_non_blocking and is_infinite are set accordingly.
"""
if hasattr(time, 'monotonic'):
# Timeout implementation with time.monotonic(). This function is only
# supported by Python 3.3 and above. It returns a time in seconds
# (float) just as time.time(), but is not affected by system clock
# adjustments.
TIME = time.monotonic
else:
# Timeout implementation with time.time(). This is compatible with all
# Python versions but has issues if the clock is adjusted while the
# timeout is running.
TIME = time.time
def __init__(self, duration):
"""Initialize a timeout with given duration"""
self.is_infinite = (duration is None)
self.is_non_blocking = (duration == 0)
self.duration = duration
if duration is not None:
self.target_time = self.TIME() + duration
else:
self.target_time = None
def expired(self):
"""Return a boolean, telling if the timeout has expired"""
return self.target_time is not None and self.time_left() <= 0
def time_left(self):
"""Return how many seconds are left until the timeout expires"""
if self.is_non_blocking:
return 0
elif self.is_infinite:
return None
else:
delta = self.target_time - self.TIME()
if delta > self.duration:
# clock jumped, recalculate
self.target_time = self.TIME() + self.duration
return self.duration
else:
return max(0, delta)
def restart(self, duration):
"""\
Restart a timeout, only supported if a timeout was already set up
before.
"""
self.duration = duration
self.target_time = self.TIME() + duration
class SerialBase(io.RawIOBase):
"""\
Serial port base class. Provides __init__ function and properties to
get/set port settings.
"""
# default values, may be overridden in subclasses that do not support all values
BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000,
576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000,
3000000, 3500000, 4000000)
BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS)
PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE)
STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO)
def __init__(self,
port=None,
baudrate=9600,
bytesize=EIGHTBITS,
parity=PARITY_NONE,
stopbits=STOPBITS_ONE,
timeout=None,
xonxoff=False,
rtscts=False,
write_timeout=None,
dsrdtr=False,
inter_byte_timeout=None,
exclusive=None,
**kwargs):
"""\
Initialize comm port object. If a "port" is given, then the port will be
opened immediately. Otherwise a Serial port object in closed state
is returned.
"""
self.is_open = False
self.portstr = None
self.name = None
# correct values are assigned below through properties
self._port = None
self._baudrate = None
self._bytesize = None
self._parity = None
self._stopbits = None
self._timeout = None
self._write_timeout = None
self._xonxoff = None
self._rtscts = None
self._dsrdtr = None
self._inter_byte_timeout = None
self._rs485_mode = None # disabled by default
self._rts_state = True
self._dtr_state = True
self._break_state = False
self._exclusive = None
# assign values using get/set methods using the properties feature
self.port = port
self.baudrate = baudrate
self.bytesize = bytesize
self.parity = parity
self.stopbits = stopbits
self.timeout = timeout
self.write_timeout = write_timeout
self.xonxoff = xonxoff
self.rtscts = rtscts
self.dsrdtr = dsrdtr
self.inter_byte_timeout = inter_byte_timeout
self.exclusive = exclusive
# watch for backward compatible kwargs
if 'writeTimeout' in kwargs:
self.write_timeout = kwargs.pop('writeTimeout')
if 'interCharTimeout' in kwargs:
self.inter_byte_timeout = kwargs.pop('interCharTimeout')
if kwargs:
raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs))
if port is not None:
self.open()
# - - - - - - - - - - - - - - - - - - - - - - - -
# to be implemented by subclasses:
# def open(self):
# def close(self):
# - - - - - - - - - - - - - - - - - - - - - - - -
@property
def port(self):
"""\
Get the current port setting. The value that was passed on init or using
setPort() is passed back.
"""
return self._port
@port.setter
def port(self, port):
"""\
Change the port.
"""
if port is not None and not isinstance(port, basestring):
raise ValueError('"port" must be None or a string, not {}'.format(type(port)))
was_open = self.is_open
if was_open:
self.close()
self.portstr = port
self._port = port
self.name = self.portstr
if was_open:
self.open()
@property
def baudrate(self):
"""Get the current baud rate setting."""
return self._baudrate
@baudrate.setter
def baudrate(self, baudrate):
"""\
Change baud rate. It raises a ValueError if the port is open and the
baud rate is not possible. If the port is closed, then the value is
accepted and the exception is raised when the port is opened.
"""
try:
b = int(baudrate)
except TypeError:
raise ValueError("Not a valid baudrate: {!r}".format(baudrate))
else:
if b < 0:
raise ValueError("Not a valid baudrate: {!r}".format(baudrate))
self._baudrate = b
if self.is_open:
self._reconfigure_port()
@property
def bytesize(self):
"""Get the current byte size setting."""
return self._bytesize
@bytesize.setter
def bytesize(self, bytesize):
"""Change byte size."""
if bytesize not in self.BYTESIZES:
raise ValueError("Not a valid byte size: {!r}".format(bytesize))
self._bytesize = bytesize
if self.is_open:
self._reconfigure_port()
@property
def exclusive(self):
"""Get the current exclusive access setting."""
return self._exclusive
@exclusive.setter
def exclusive(self, exclusive):
"""Change the exclusive access setting."""
self._exclusive = exclusive
if self.is_open:
self._reconfigure_port()
@property
def parity(self):
"""Get the current parity setting."""
return self._parity
@parity.setter
def parity(self, parity):
"""Change parity setting."""
if parity not in self.PARITIES:
raise ValueError("Not a valid parity: {!r}".format(parity))
self._parity = parity
if self.is_open:
self._reconfigure_port()
@property
def stopbits(self):
"""Get the current stop bits setting."""
return self._stopbits
@stopbits.setter
def stopbits(self, stopbits):
"""Change stop bits size."""
if stopbits not in self.STOPBITS:
raise ValueError("Not a valid stop bit size: {!r}".format(stopbits))
self._stopbits = stopbits
if self.is_open:
self._reconfigure_port()
@property
def timeout(self):
"""Get the current timeout setting."""
return self._timeout
@timeout.setter
def timeout(self, timeout):
"""Change timeout setting."""
if timeout is not None:
try:
timeout + 1 # test if it's a number, will throw a TypeError if not...
except TypeError:
raise ValueError("Not a valid timeout: {!r}".format(timeout))
if timeout < 0:
raise ValueError("Not a valid timeout: {!r}".format(timeout))
self._timeout = timeout
if self.is_open:
self._reconfigure_port()
@property
def write_timeout(self):
"""Get the current timeout setting."""
return self._write_timeout
@write_timeout.setter
def write_timeout(self, timeout):
"""Change timeout setting."""
if timeout is not None:
if timeout < 0:
raise ValueError("Not a valid timeout: {!r}".format(timeout))
try:
timeout + 1 # test if it's a number, will throw a TypeError if not...
except TypeError:
raise ValueError("Not a valid timeout: {!r}".format(timeout))
self._write_timeout = timeout
if self.is_open:
self._reconfigure_port()
@property
def inter_byte_timeout(self):
"""Get the current inter-character timeout setting."""
return self._inter_byte_timeout
@inter_byte_timeout.setter
def inter_byte_timeout(self, ic_timeout):
"""Change inter-byte timeout setting."""
if ic_timeout is not None:
if ic_timeout < 0:
raise ValueError("Not a valid timeout: {!r}".format(ic_timeout))
try:
ic_timeout + 1 # test if it's a number, will throw a TypeError if not...
except TypeError:
raise ValueError("Not a valid timeout: {!r}".format(ic_timeout))
self._inter_byte_timeout = ic_timeout
if self.is_open:
self._reconfigure_port()
@property
def xonxoff(self):
"""Get the current XON/XOFF setting."""
return self._xonxoff
@xonxoff.setter
def xonxoff(self, xonxoff):
"""Change XON/XOFF setting."""
self._xonxoff = xonxoff
if self.is_open:
self._reconfigure_port()
@property
def rtscts(self):
"""Get the current RTS/CTS flow control setting."""
return self._rtscts
@rtscts.setter
def rtscts(self, rtscts):
"""Change RTS/CTS flow control setting."""
self._rtscts = rtscts
if self.is_open:
self._reconfigure_port()
@property
def dsrdtr(self):
"""Get the current DSR/DTR flow control setting."""
return self._dsrdtr
@dsrdtr.setter
def dsrdtr(self, dsrdtr=None):
"""Change DsrDtr flow control setting."""
if dsrdtr is None:
# if not set, keep backwards compatibility and follow rtscts setting
self._dsrdtr = self._rtscts
else:
# if defined independently, follow its value
self._dsrdtr = dsrdtr
if self.is_open:
self._reconfigure_port()
@property
def rts(self):
return self._rts_state
@rts.setter
def rts(self, value):
self._rts_state = value
if self.is_open:
self._update_rts_state()
@property
def dtr(self):
return self._dtr_state
@dtr.setter
def dtr(self, value):
self._dtr_state = value
if self.is_open:
self._update_dtr_state()
@property
def break_condition(self):
return self._break_state
@break_condition.setter
def break_condition(self, value):
self._break_state = value
if self.is_open:
self._update_break_state()
# - - - - - - - - - - - - - - - - - - - - - - - -
# functions useful for RS-485 adapters
@property
def rs485_mode(self):
"""\
Enable RS485 mode and apply new settings, set to None to disable.
See serial.rs485.RS485Settings for more info about the value.
"""
return self._rs485_mode
@rs485_mode.setter
def rs485_mode(self, rs485_settings):
self._rs485_mode = rs485_settings
if self.is_open:
self._reconfigure_port()
# - - - - - - - - - - - - - - - - - - - - - - - -
_SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff',
'dsrdtr', 'rtscts', 'timeout', 'write_timeout',
'inter_byte_timeout')
def get_settings(self):
"""\
Get current port settings as a dictionary. For use with
apply_settings().
"""
return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS])
def apply_settings(self, d):
"""\
Apply stored settings from a dictionary returned from
get_settings(). It's allowed to delete keys from the dictionary. These
values will simply left unchanged.
"""
for key in self._SAVED_SETTINGS:
if key in d and d[key] != getattr(self, '_' + key): # check against internal "_" value
setattr(self, key, d[key]) # set non "_" value to use properties write function
# - - - - - - - - - - - - - - - - - - - - - - - -
def __repr__(self):
"""String representation of the current port settings and its state."""
return '{name}<id=0x{id:x}, open={p.is_open}>(port={p.portstr!r}, ' \
'baudrate={p.baudrate!r}, bytesize={p.bytesize!r}, parity={p.parity!r}, ' \
'stopbits={p.stopbits!r}, timeout={p.timeout!r}, xonxoff={p.xonxoff!r}, ' \
'rtscts={p.rtscts!r}, dsrdtr={p.dsrdtr!r})'.format(
name=self.__class__.__name__, id=id(self), p=self)
# - - - - - - - - - - - - - - - - - - - - - - - -
# compatibility with io library
# pylint: disable=invalid-name,missing-docstring
def readable(self):
return True
def writable(self):
return True
def seekable(self):
return False
def readinto(self, b):
data = self.read(len(b))
n = len(data)
try:
b[:n] = data
except TypeError as err:
import array
if not isinstance(b, array.array):
raise err
b[:n] = array.array('b', data)
return n
def close(self):
# Do not call RawIOBase.close() as that will try to flush().
pass
@property
def closed(self):
# Overrides RawIOBase.closed, as RawIOBase can only be closed once,
# but a Serial object can be opened/closed multiple times.
return not self.is_open
# - - - - - - - - - - - - - - - - - - - - - - - -
# context manager
def __enter__(self):
if self._port is not None and not self.is_open:
self.open()
return self
def __exit__(self, *args, **kwargs):
self.close()
# - - - - - - - - - - - - - - - - - - - - - - - -
def send_break(self, duration=0.25):
"""\
Send break condition. Timed, returns to idle state after given
duration.
"""
if not self.is_open:
raise PortNotOpenError()
self.break_condition = True
time.sleep(duration)
self.break_condition = False
# - - - - - - - - - - - - - - - - - - - - - - - -
# backwards compatibility / deprecated functions
def flushInput(self):
self.reset_input_buffer()
def flushOutput(self):
self.reset_output_buffer()
def inWaiting(self):
return self.in_waiting
def sendBreak(self, duration=0.25):
self.send_break(duration)
def setRTS(self, value=1):
self.rts = value
def setDTR(self, value=1):
self.dtr = value
def getCTS(self):
return self.cts
def getDSR(self):
return self.dsr
def getRI(self):
return self.ri
def getCD(self):
return self.cd
def setPort(self, port):
self.port = port
@property
def writeTimeout(self):
return self.write_timeout
@writeTimeout.setter
def writeTimeout(self, timeout):
self.write_timeout = timeout
@property
def interCharTimeout(self):
return self.inter_byte_timeout
@interCharTimeout.setter
def interCharTimeout(self, interCharTimeout):
self.inter_byte_timeout = interCharTimeout
def getSettingsDict(self):
return self.get_settings()
def applySettingsDict(self, d):
self.apply_settings(d)
def isOpen(self):
return self.is_open
# - - - - - - - - - - - - - - - - - - - - - - - -
# additional functionality
def read_all(self):
"""\
Read all bytes currently available in the buffer of the OS.
"""
return self.read(self.in_waiting)
def read_until(self, expected=LF, size=None):
"""\
Read until an expected sequence is found (line feed by default), the size
is exceeded or until timeout occurs.
"""
lenterm = len(expected)
line = bytearray()
timeout = Timeout(self._timeout)
while True:
c = self.read(1)
if c:
line += c
if line[-lenterm:] == expected:
break
if size is not None and len(line) >= size:
break
else:
break
if timeout.expired():
break
return bytes(line)
def iread_until(self, *args, **kwargs):
"""\
Read lines, implemented as generator. It will raise StopIteration on
timeout (empty read).
"""
while True:
line = self.read_until(*args, **kwargs)
if not line:
break
yield line
# - - - - - - - - - - - - - - - - - - - - - - - - -
if __name__ == '__main__':
import sys
s = SerialBase()
sys.stdout.write('port name: {}\n'.format(s.name))
sys.stdout.write('baud rates: {}\n'.format(s.BAUDRATES))
sys.stdout.write('byte sizes: {}\n'.format(s.BYTESIZES))
sys.stdout.write('parities: {}\n'.format(s.PARITIES))
sys.stdout.write('stop bits: {}\n'.format(s.STOPBITS))
sys.stdout.write('{}\n'.format(s))
-477
View File
@@ -1,477 +0,0 @@
#! python
#
# backend for Windows ("win32" incl. 32/64 bit support)
#
# (C) 2001-2020 Chris Liechti <cliechti@gmx.net>
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# SPDX-License-Identifier: BSD-3-Clause
#
# Initial patch to use ctypes by Giovanni Bajo <rasky@develer.com>
from __future__ import absolute_import
# pylint: disable=invalid-name,too-few-public-methods
import ctypes
import time
from serial import win32
import serial
from serial.serialutil import SerialBase, SerialException, to_bytes, PortNotOpenError, SerialTimeoutException
class Serial(SerialBase):
"""Serial port implementation for Win32 based on ctypes."""
BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200)
def __init__(self, *args, **kwargs):
self._port_handle = None
self._overlapped_read = None
self._overlapped_write = None
super(Serial, self).__init__(*args, **kwargs)
def open(self):
"""\
Open port with current settings. This may throw a SerialException
if the port cannot be opened.
"""
if self._port is None:
raise SerialException("Port must be configured before it can be used.")
if self.is_open:
raise SerialException("Port is already open.")
# the "\\.\COMx" format is required for devices other than COM1-COM8
# not all versions of windows seem to support this properly
# so that the first few ports are used with the DOS device name
port = self.name
try:
if port.upper().startswith('COM') and int(port[3:]) > 8:
port = '\\\\.\\' + port
except ValueError:
# for like COMnotanumber
pass
self._port_handle = win32.CreateFile(
port,
win32.GENERIC_READ | win32.GENERIC_WRITE,
0, # exclusive access
None, # no security
win32.OPEN_EXISTING,
win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED,
0)
if self._port_handle == win32.INVALID_HANDLE_VALUE:
self._port_handle = None # 'cause __del__ is called anyway
raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError()))
try:
self._overlapped_read = win32.OVERLAPPED()
self._overlapped_read.hEvent = win32.CreateEvent(None, 1, 0, None)
self._overlapped_write = win32.OVERLAPPED()
#~ self._overlapped_write.hEvent = win32.CreateEvent(None, 1, 0, None)
self._overlapped_write.hEvent = win32.CreateEvent(None, 0, 0, None)
# Setup a 4k buffer
win32.SetupComm(self._port_handle, 4096, 4096)
# Save original timeout values:
self._orgTimeouts = win32.COMMTIMEOUTS()
win32.GetCommTimeouts(self._port_handle, ctypes.byref(self._orgTimeouts))
self._reconfigure_port()
# Clear buffers:
# Remove anything that was there
win32.PurgeComm(
self._port_handle,
win32.PURGE_TXCLEAR | win32.PURGE_TXABORT |
win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
except:
try:
self._close()
except:
# ignore any exception when closing the port
# also to keep original exception that happened when setting up
pass
self._port_handle = None
raise
else:
self.is_open = True
def _reconfigure_port(self):
"""Set communication parameters on opened port."""
if not self._port_handle:
raise SerialException("Can only operate on a valid port handle")
# Set Windows timeout values
# timeouts is a tuple with the following items:
# (ReadIntervalTimeout,ReadTotalTimeoutMultiplier,
# ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier,
# WriteTotalTimeoutConstant)
timeouts = win32.COMMTIMEOUTS()
if self._timeout is None:
pass # default of all zeros is OK
elif self._timeout == 0:
timeouts.ReadIntervalTimeout = win32.MAXDWORD
else:
timeouts.ReadTotalTimeoutConstant = max(int(self._timeout * 1000), 1)
if self._timeout != 0 and self._inter_byte_timeout is not None:
timeouts.ReadIntervalTimeout = max(int(self._inter_byte_timeout * 1000), 1)
if self._write_timeout is None:
pass
elif self._write_timeout == 0:
timeouts.WriteTotalTimeoutConstant = win32.MAXDWORD
else:
timeouts.WriteTotalTimeoutConstant = max(int(self._write_timeout * 1000), 1)
win32.SetCommTimeouts(self._port_handle, ctypes.byref(timeouts))
win32.SetCommMask(self._port_handle, win32.EV_ERR)
# Setup the connection info.
# Get state and modify it:
comDCB = win32.DCB()
win32.GetCommState(self._port_handle, ctypes.byref(comDCB))
comDCB.BaudRate = self._baudrate
if self._bytesize == serial.FIVEBITS:
comDCB.ByteSize = 5
elif self._bytesize == serial.SIXBITS:
comDCB.ByteSize = 6
elif self._bytesize == serial.SEVENBITS:
comDCB.ByteSize = 7
elif self._bytesize == serial.EIGHTBITS:
comDCB.ByteSize = 8
else:
raise ValueError("Unsupported number of data bits: {!r}".format(self._bytesize))
if self._parity == serial.PARITY_NONE:
comDCB.Parity = win32.NOPARITY
comDCB.fParity = 0 # Disable Parity Check
elif self._parity == serial.PARITY_EVEN:
comDCB.Parity = win32.EVENPARITY
comDCB.fParity = 1 # Enable Parity Check
elif self._parity == serial.PARITY_ODD:
comDCB.Parity = win32.ODDPARITY
comDCB.fParity = 1 # Enable Parity Check
elif self._parity == serial.PARITY_MARK:
comDCB.Parity = win32.MARKPARITY
comDCB.fParity = 1 # Enable Parity Check
elif self._parity == serial.PARITY_SPACE:
comDCB.Parity = win32.SPACEPARITY
comDCB.fParity = 1 # Enable Parity Check
else:
raise ValueError("Unsupported parity mode: {!r}".format(self._parity))
if self._stopbits == serial.STOPBITS_ONE:
comDCB.StopBits = win32.ONESTOPBIT
elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE:
comDCB.StopBits = win32.ONE5STOPBITS
elif self._stopbits == serial.STOPBITS_TWO:
comDCB.StopBits = win32.TWOSTOPBITS
else:
raise ValueError("Unsupported number of stop bits: {!r}".format(self._stopbits))
comDCB.fBinary = 1 # Enable Binary Transmission
# Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE)
if self._rs485_mode is None:
if self._rtscts:
comDCB.fRtsControl = win32.RTS_CONTROL_HANDSHAKE
else:
comDCB.fRtsControl = win32.RTS_CONTROL_ENABLE if self._rts_state else win32.RTS_CONTROL_DISABLE
comDCB.fOutxCtsFlow = self._rtscts
else:
# checks for unsupported settings
# XXX verify if platform really does not have a setting for those
if not self._rs485_mode.rts_level_for_tx:
raise ValueError(
'Unsupported value for RS485Settings.rts_level_for_tx: {!r} (only True is allowed)'.format(
self._rs485_mode.rts_level_for_tx,))
if self._rs485_mode.rts_level_for_rx:
raise ValueError(
'Unsupported value for RS485Settings.rts_level_for_rx: {!r} (only False is allowed)'.format(
self._rs485_mode.rts_level_for_rx,))
if self._rs485_mode.delay_before_tx is not None:
raise ValueError(
'Unsupported value for RS485Settings.delay_before_tx: {!r} (only None is allowed)'.format(
self._rs485_mode.delay_before_tx,))
if self._rs485_mode.delay_before_rx is not None:
raise ValueError(
'Unsupported value for RS485Settings.delay_before_rx: {!r} (only None is allowed)'.format(
self._rs485_mode.delay_before_rx,))
if self._rs485_mode.loopback:
raise ValueError(
'Unsupported value for RS485Settings.loopback: {!r} (only False is allowed)'.format(
self._rs485_mode.loopback,))
comDCB.fRtsControl = win32.RTS_CONTROL_TOGGLE
comDCB.fOutxCtsFlow = 0
if self._dsrdtr:
comDCB.fDtrControl = win32.DTR_CONTROL_HANDSHAKE
else:
comDCB.fDtrControl = win32.DTR_CONTROL_ENABLE if self._dtr_state else win32.DTR_CONTROL_DISABLE
comDCB.fOutxDsrFlow = self._dsrdtr
comDCB.fOutX = self._xonxoff
comDCB.fInX = self._xonxoff
comDCB.fNull = 0
comDCB.fErrorChar = 0
comDCB.fAbortOnError = 0
comDCB.XonChar = serial.XON
comDCB.XoffChar = serial.XOFF
if not win32.SetCommState(self._port_handle, ctypes.byref(comDCB)):
raise SerialException(
'Cannot configure port, something went wrong. '
'Original message: {!r}'.format(ctypes.WinError()))
#~ def __del__(self):
#~ self.close()
def _close(self):
"""internal close port helper"""
if self._port_handle is not None:
# Restore original timeout values:
win32.SetCommTimeouts(self._port_handle, self._orgTimeouts)
if self._overlapped_read is not None:
self.cancel_read()
win32.CloseHandle(self._overlapped_read.hEvent)
self._overlapped_read = None
if self._overlapped_write is not None:
self.cancel_write()
win32.CloseHandle(self._overlapped_write.hEvent)
self._overlapped_write = None
win32.CloseHandle(self._port_handle)
self._port_handle = None
def close(self):
"""Close port"""
if self.is_open:
self._close()
self.is_open = False
# - - - - - - - - - - - - - - - - - - - - - - - -
@property
def in_waiting(self):
"""Return the number of bytes currently in the input buffer."""
flags = win32.DWORD()
comstat = win32.COMSTAT()
if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)):
raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
return comstat.cbInQue
def read(self, size=1):
"""\
Read size bytes from the serial port. If a timeout is set it may
return less characters as requested. With no timeout it will block
until the requested number of bytes is read.
"""
if not self.is_open:
raise PortNotOpenError()
if size > 0:
win32.ResetEvent(self._overlapped_read.hEvent)
flags = win32.DWORD()
comstat = win32.COMSTAT()
if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)):
raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
n = min(comstat.cbInQue, size) if self.timeout == 0 else size
if n > 0:
buf = ctypes.create_string_buffer(n)
rc = win32.DWORD()
read_ok = win32.ReadFile(
self._port_handle,
buf,
n,
ctypes.byref(rc),
ctypes.byref(self._overlapped_read))
if not read_ok and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING):
raise SerialException("ReadFile failed ({!r})".format(ctypes.WinError()))
result_ok = win32.GetOverlappedResult(
self._port_handle,
ctypes.byref(self._overlapped_read),
ctypes.byref(rc),
True)
if not result_ok:
if win32.GetLastError() != win32.ERROR_OPERATION_ABORTED:
raise SerialException("GetOverlappedResult failed ({!r})".format(ctypes.WinError()))
read = buf.raw[:rc.value]
else:
read = bytes()
else:
read = bytes()
return bytes(read)
def write(self, data):
"""Output the given byte string over the serial port."""
if not self.is_open:
raise PortNotOpenError()
#~ if not isinstance(data, (bytes, bytearray)):
#~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
# convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview
data = to_bytes(data)
if data:
#~ win32event.ResetEvent(self._overlapped_write.hEvent)
n = win32.DWORD()
success = win32.WriteFile(self._port_handle, data, len(data), ctypes.byref(n), self._overlapped_write)
if self._write_timeout != 0: # if blocking (None) or w/ write timeout (>0)
if not success and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING):
raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError()))
# Wait for the write to complete.
#~ win32.WaitForSingleObject(self._overlapped_write.hEvent, win32.INFINITE)
win32.GetOverlappedResult(self._port_handle, self._overlapped_write, ctypes.byref(n), True)
if win32.GetLastError() == win32.ERROR_OPERATION_ABORTED:
return n.value # canceled IO is no error
if n.value != len(data):
raise SerialTimeoutException('Write timeout')
return n.value
else:
errorcode = win32.ERROR_SUCCESS if success else win32.GetLastError()
if errorcode in (win32.ERROR_INVALID_USER_BUFFER, win32.ERROR_NOT_ENOUGH_MEMORY,
win32.ERROR_OPERATION_ABORTED):
return 0
elif errorcode in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING):
# no info on true length provided by OS function in async mode
return len(data)
else:
raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError()))
else:
return 0
def flush(self):
"""\
Flush of file like objects. In this case, wait until all data
is written.
"""
while self.out_waiting:
time.sleep(0.05)
# XXX could also use WaitCommEvent with mask EV_TXEMPTY, but it would
# require overlapped IO and it's also only possible to set a single mask
# on the port---
def reset_input_buffer(self):
"""Clear input buffer, discarding all that is in the buffer."""
if not self.is_open:
raise PortNotOpenError()
win32.PurgeComm(self._port_handle, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
def reset_output_buffer(self):
"""\
Clear output buffer, aborting the current output and discarding all
that is in the buffer.
"""
if not self.is_open:
raise PortNotOpenError()
win32.PurgeComm(self._port_handle, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT)
def _update_break_state(self):
"""Set break: Controls TXD. When active, to transmitting is possible."""
if not self.is_open:
raise PortNotOpenError()
if self._break_state:
win32.SetCommBreak(self._port_handle)
else:
win32.ClearCommBreak(self._port_handle)
def _update_rts_state(self):
"""Set terminal status line: Request To Send"""
if self._rts_state:
win32.EscapeCommFunction(self._port_handle, win32.SETRTS)
else:
win32.EscapeCommFunction(self._port_handle, win32.CLRRTS)
def _update_dtr_state(self):
"""Set terminal status line: Data Terminal Ready"""
if self._dtr_state:
win32.EscapeCommFunction(self._port_handle, win32.SETDTR)
else:
win32.EscapeCommFunction(self._port_handle, win32.CLRDTR)
def _GetCommModemStatus(self):
if not self.is_open:
raise PortNotOpenError()
stat = win32.DWORD()
win32.GetCommModemStatus(self._port_handle, ctypes.byref(stat))
return stat.value
@property
def cts(self):
"""Read terminal status line: Clear To Send"""
return win32.MS_CTS_ON & self._GetCommModemStatus() != 0
@property
def dsr(self):
"""Read terminal status line: Data Set Ready"""
return win32.MS_DSR_ON & self._GetCommModemStatus() != 0
@property
def ri(self):
"""Read terminal status line: Ring Indicator"""
return win32.MS_RING_ON & self._GetCommModemStatus() != 0
@property
def cd(self):
"""Read terminal status line: Carrier Detect"""
return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0
# - - platform specific - - - -
def set_buffer_size(self, rx_size=4096, tx_size=None):
"""\
Recommend a buffer size to the driver (device driver can ignore this
value). Must be called after the port is opened.
"""
if tx_size is None:
tx_size = rx_size
win32.SetupComm(self._port_handle, rx_size, tx_size)
def set_output_flow_control(self, enable=True):
"""\
Manually control flow - when software flow control is enabled.
This will do the same as if XON (true) or XOFF (false) are received
from the other device and control the transmission accordingly.
WARNING: this function is not portable to different platforms!
"""
if not self.is_open:
raise PortNotOpenError()
if enable:
win32.EscapeCommFunction(self._port_handle, win32.SETXON)
else:
win32.EscapeCommFunction(self._port_handle, win32.SETXOFF)
@property
def out_waiting(self):
"""Return how many bytes the in the outgoing buffer"""
flags = win32.DWORD()
comstat = win32.COMSTAT()
if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)):
raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
return comstat.cbOutQue
def _cancel_overlapped_io(self, overlapped):
"""Cancel a blocking read operation, may be called from other thread"""
# check if read operation is pending
rc = win32.DWORD()
err = win32.GetOverlappedResult(
self._port_handle,
ctypes.byref(overlapped),
ctypes.byref(rc),
False)
if not err and win32.GetLastError() in (win32.ERROR_IO_PENDING, win32.ERROR_IO_INCOMPLETE):
# cancel, ignoring any errors (e.g. it may just have finished on its own)
win32.CancelIoEx(self._port_handle, overlapped)
def cancel_read(self):
"""Cancel a blocking read operation, may be called from other thread"""
self._cancel_overlapped_io(self._overlapped_read)
def cancel_write(self):
"""Cancel a blocking write operation, may be called from other thread"""
self._cancel_overlapped_io(self._overlapped_write)
@SerialBase.exclusive.setter
def exclusive(self, exclusive):
"""Change the exclusive access setting."""
if exclusive is not None and not exclusive:
raise ValueError('win32 only supports exclusive access (not: {})'.format(exclusive))
else:
serial.SerialBase.exclusive.__set__(self, exclusive)
-297
View File
@@ -1,297 +0,0 @@
#!/usr/bin/env python3
#
# Working with threading and pySerial
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2015-2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Support threading with serial ports.
"""
from __future__ import absolute_import
import serial
import threading
class Protocol(object):
"""\
Protocol as used by the ReaderThread. This base class provides empty
implementations of all methods.
"""
def connection_made(self, transport):
"""Called when reader thread is started"""
def data_received(self, data):
"""Called with snippets received from the serial port"""
def connection_lost(self, exc):
"""\
Called when the serial port is closed or the reader loop terminated
otherwise.
"""
if isinstance(exc, Exception):
raise exc
class Packetizer(Protocol):
"""
Read binary packets from serial port. Packets are expected to be terminated
with a TERMINATOR byte (null byte by default).
The class also keeps track of the transport.
"""
TERMINATOR = b'\0'
def __init__(self):
self.buffer = bytearray()
self.transport = None
def connection_made(self, transport):
"""Store transport"""
self.transport = transport
def connection_lost(self, exc):
"""Forget transport"""
self.transport = None
super(Packetizer, self).connection_lost(exc)
def data_received(self, data):
"""Buffer received data, find TERMINATOR, call handle_packet"""
self.buffer.extend(data)
while self.TERMINATOR in self.buffer:
packet, self.buffer = self.buffer.split(self.TERMINATOR, 1)
self.handle_packet(packet)
def handle_packet(self, packet):
"""Process packets - to be overridden by subclassing"""
raise NotImplementedError('please implement functionality in handle_packet')
class FramedPacket(Protocol):
"""
Read binary packets. Packets are expected to have a start and stop marker.
The class also keeps track of the transport.
"""
START = b'('
STOP = b')'
def __init__(self):
self.packet = bytearray()
self.in_packet = False
self.transport = None
def connection_made(self, transport):
"""Store transport"""
self.transport = transport
def connection_lost(self, exc):
"""Forget transport"""
self.transport = None
self.in_packet = False
del self.packet[:]
super(FramedPacket, self).connection_lost(exc)
def data_received(self, data):
"""Find data enclosed in START/STOP, call handle_packet"""
for byte in serial.iterbytes(data):
if byte == self.START:
self.in_packet = True
elif byte == self.STOP:
self.in_packet = False
self.handle_packet(bytes(self.packet)) # make read-only copy
del self.packet[:]
elif self.in_packet:
self.packet.extend(byte)
else:
self.handle_out_of_packet_data(byte)
def handle_packet(self, packet):
"""Process packets - to be overridden by subclassing"""
raise NotImplementedError('please implement functionality in handle_packet')
def handle_out_of_packet_data(self, data):
"""Process data that is received outside of packets"""
pass
class LineReader(Packetizer):
"""
Read and write (Unicode) lines from/to serial port.
The encoding is applied.
"""
TERMINATOR = b'\r\n'
ENCODING = 'utf-8'
UNICODE_HANDLING = 'replace'
def handle_packet(self, packet):
self.handle_line(packet.decode(self.ENCODING, self.UNICODE_HANDLING))
def handle_line(self, line):
"""Process one line - to be overridden by subclassing"""
raise NotImplementedError('please implement functionality in handle_line')
def write_line(self, text):
"""
Write text to the transport. ``text`` is a Unicode string and the encoding
is applied before sending and also the newline is append.
"""
# + is not the best choice but bytes does not support % or .format in py3 and we want a single write call
self.transport.write(text.encode(self.ENCODING, self.UNICODE_HANDLING) + self.TERMINATOR)
class ReaderThread(threading.Thread):
"""\
Implement a serial port read loop and dispatch to a Protocol instance (like
the asyncio.Protocol) but do it with threads.
Calls to close() will close the serial port but it is also possible to just
stop() this thread and continue the serial port instance otherwise.
"""
def __init__(self, serial_instance, protocol_factory):
"""\
Initialize thread.
Note that the serial_instance' timeout is set to one second!
Other settings are not changed.
"""
super(ReaderThread, self).__init__()
self.daemon = True
self.serial = serial_instance
self.protocol_factory = protocol_factory
self.alive = True
self._lock = threading.Lock()
self._connection_made = threading.Event()
self.protocol = None
def stop(self):
"""Stop the reader thread"""
self.alive = False
if hasattr(self.serial, 'cancel_read'):
self.serial.cancel_read()
self.join(2)
def run(self):
"""Reader loop"""
if not hasattr(self.serial, 'cancel_read'):
self.serial.timeout = 1
self.protocol = self.protocol_factory()
try:
self.protocol.connection_made(self)
except Exception as e:
self.alive = False
self.protocol.connection_lost(e)
self._connection_made.set()
return
error = None
self._connection_made.set()
while self.alive and self.serial.is_open:
try:
# read all that is there or wait for one byte (blocking)
data = self.serial.read(self.serial.in_waiting or 1)
except serial.SerialException as e:
# probably some I/O problem such as disconnected USB serial
# adapters -> exit
error = e
break
else:
if data:
# make a separated try-except for called user code
try:
self.protocol.data_received(data)
except Exception as e:
error = e
break
self.alive = False
self.protocol.connection_lost(error)
self.protocol = None
def write(self, data):
"""Thread safe writing (uses lock)"""
with self._lock:
return self.serial.write(data)
def close(self):
"""Close the serial port and exit reader thread (uses lock)"""
# use the lock to let other threads finish writing
with self._lock:
# first stop reading, so that closing can be done on idle port
self.stop()
self.serial.close()
def connect(self):
"""
Wait until connection is set up and return the transport and protocol
instances.
"""
if self.alive:
self._connection_made.wait()
if not self.alive:
raise RuntimeError('connection_lost already called')
return (self, self.protocol)
else:
raise RuntimeError('already stopped')
# - - context manager, returns protocol
def __enter__(self):
"""\
Enter context handler. May raise RuntimeError in case the connection
could not be created.
"""
self.start()
self._connection_made.wait()
if not self.alive:
raise RuntimeError('connection_lost already called')
return self.protocol
def __exit__(self, exc_type, exc_val, exc_tb):
"""Leave context: close port"""
self.close()
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# test
if __name__ == '__main__':
# pylint: disable=wrong-import-position
import sys
import time
import traceback
#~ PORT = 'spy:///dev/ttyUSB0'
PORT = 'loop://'
class PrintLines(LineReader):
def connection_made(self, transport):
super(PrintLines, self).connection_made(transport)
sys.stdout.write('port opened\n')
self.write_line('hello world')
def handle_line(self, data):
sys.stdout.write('line received: {!r}\n'.format(data))
def connection_lost(self, exc):
if exc:
traceback.print_exc(exc)
sys.stdout.write('port closed\n')
ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1)
with ReaderThread(ser, PrintLines) as protocol:
protocol.write_line('hello')
time.sleep(2)
# alternative usage
ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1)
t = ReaderThread(ser, PrintLines)
t.start()
transport, protocol = t.connect()
protocol.write_line('hello')
time.sleep(2)
t.close()
View File
-126
View File
@@ -1,126 +0,0 @@
#! python
#
# This is a codec to create and decode hexdumps with spaces between characters. used by miniterm.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2015-2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
Python 'hex' Codec - 2-digit hex with spaces content transfer encoding.
Encode and decode may be a bit misleading at first sight...
The textual representation is a hex dump: e.g. "40 41"
The "encoded" data of this is the binary form, e.g. b"@A"
Therefore decoding is binary to text and thus converting binary data to hex dump.
"""
from __future__ import absolute_import
import codecs
import serial
try:
unicode
except (NameError, AttributeError):
unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name
HEXDIGITS = '0123456789ABCDEF'
# Codec APIs
def hex_encode(data, errors='strict'):
"""'40 41 42' -> b'@ab'"""
return (serial.to_bytes([int(h, 16) for h in data.split()]), len(data))
def hex_decode(data, errors='strict'):
"""b'@ab' -> '40 41 42'"""
return (unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))), len(data))
class Codec(codecs.Codec):
def encode(self, data, errors='strict'):
"""'40 41 42' -> b'@ab'"""
return serial.to_bytes([int(h, 16) for h in data.split()])
def decode(self, data, errors='strict'):
"""b'@ab' -> '40 41 42'"""
return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data)))
class IncrementalEncoder(codecs.IncrementalEncoder):
"""Incremental hex encoder"""
def __init__(self, errors='strict'):
self.errors = errors
self.state = 0
def reset(self):
self.state = 0
def getstate(self):
return self.state
def setstate(self, state):
self.state = state
def encode(self, data, final=False):
"""\
Incremental encode, keep track of digits and emit a byte when a pair
of hex digits is found. The space is optional unless the error
handling is defined to be 'strict'.
"""
state = self.state
encoded = []
for c in data.upper():
if c in HEXDIGITS:
z = HEXDIGITS.index(c)
if state:
encoded.append(z + (state & 0xf0))
state = 0
else:
state = 0x100 + (z << 4)
elif c == ' ': # allow spaces to separate values
if state and self.errors == 'strict':
raise UnicodeError('odd number of hex digits')
state = 0
else:
if self.errors == 'strict':
raise UnicodeError('non-hex digit found: {!r}'.format(c))
self.state = state
return serial.to_bytes(encoded)
class IncrementalDecoder(codecs.IncrementalDecoder):
"""Incremental decoder"""
def decode(self, data, final=False):
return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data)))
class StreamWriter(Codec, codecs.StreamWriter):
"""Combination of hexlify codec and StreamWriter"""
class StreamReader(Codec, codecs.StreamReader):
"""Combination of hexlify codec and StreamReader"""
def getregentry():
"""encodings module API"""
return codecs.CodecInfo(
name='hexlify',
encode=hex_encode,
decode=hex_decode,
incrementalencoder=IncrementalEncoder,
incrementaldecoder=IncrementalDecoder,
streamwriter=StreamWriter,
streamreader=StreamReader,
#~ _is_text_encoding=True,
)
-110
View File
@@ -1,110 +0,0 @@
#!/usr/bin/env python
#
# Serial port enumeration. Console tool and backend selection.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
This module will provide a function called comports that returns an
iterable (generator or list) that will enumerate available com ports. Note that
on some systems non-existent ports may be listed.
Additionally a grep function is supplied that can be used to search for ports
based on their descriptions or hardware ID.
"""
from __future__ import absolute_import
import sys
import os
import re
# chose an implementation, depending on os
#~ if sys.platform == 'cli':
#~ else:
if os.name == 'nt': # sys.platform == 'win32':
from serial.tools.list_ports_windows import comports
elif os.name == 'posix':
from serial.tools.list_ports_posix import comports
#~ elif os.name == 'java':
else:
raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def grep(regexp, include_links=False):
"""\
Search for ports using a regular expression. Port name, description and
hardware ID are searched. The function returns an iterable that returns the
same tuples as comport() would do.
"""
r = re.compile(regexp, re.I)
for info in comports(include_links):
port, desc, hwid = info
if r.search(port) or r.search(desc) or r.search(hwid):
yield info
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def main():
import argparse
parser = argparse.ArgumentParser(description='Serial port enumeration')
parser.add_argument(
'regexp',
nargs='?',
help='only show ports that match this regex')
parser.add_argument(
'-v', '--verbose',
action='store_true',
help='show more messages')
parser.add_argument(
'-q', '--quiet',
action='store_true',
help='suppress all messages')
parser.add_argument(
'-n',
type=int,
help='only output the N-th entry')
parser.add_argument(
'-s', '--include-links',
action='store_true',
help='include entries that are symlinks to real devices')
args = parser.parse_args()
hits = 0
# get iteraror w/ or w/o filter
if args.regexp:
if not args.quiet:
sys.stderr.write("Filtered list with regexp: {!r}\n".format(args.regexp))
iterator = sorted(grep(args.regexp, include_links=args.include_links))
else:
iterator = sorted(comports(include_links=args.include_links))
# list them
for n, (port, desc, hwid) in enumerate(iterator, 1):
if args.n is None or args.n == n:
sys.stdout.write("{:20}\n".format(port))
if args.verbose:
sys.stdout.write(" desc: {}\n".format(desc))
sys.stdout.write(" hwid: {}\n".format(hwid))
hits += 1
if not args.quiet:
if hits:
sys.stderr.write("{} ports found\n".format(hits))
else:
sys.stderr.write("no ports found\n")
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# test
if __name__ == '__main__':
main()
-121
View File
@@ -1,121 +0,0 @@
#!/usr/bin/env python
#
# This is a helper module for the various platform dependent list_port
# implementations.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import absolute_import
import re
import glob
import os
import os.path
def numsplit(text):
"""\
Convert string into a list of texts and numbers in order to support a
natural sorting.
"""
result = []
for group in re.split(r'(\d+)', text):
if group:
try:
group = int(group)
except ValueError:
pass
result.append(group)
return result
class ListPortInfo(object):
"""Info collection base class for serial ports"""
def __init__(self, device, skip_link_detection=False):
self.device = device
self.name = os.path.basename(device)
self.description = 'n/a'
self.hwid = 'n/a'
# USB specific data
self.vid = None
self.pid = None
self.serial_number = None
self.location = None
self.manufacturer = None
self.product = None
self.interface = None
# special handling for links
if not skip_link_detection and device is not None and os.path.islink(device):
self.hwid = 'LINK={}'.format(os.path.realpath(device))
def usb_description(self):
"""return a short string to name the port based on USB info"""
if self.interface is not None:
return '{} - {}'.format(self.product, self.interface)
elif self.product is not None:
return self.product
else:
return self.name
def usb_info(self):
"""return a string with USB related information about device"""
return 'USB VID:PID={:04X}:{:04X}{}{}'.format(
self.vid or 0,
self.pid or 0,
' SER={}'.format(self.serial_number) if self.serial_number is not None else '',
' LOCATION={}'.format(self.location) if self.location is not None else '')
def apply_usb_info(self):
"""update description and hwid from USB data"""
self.description = self.usb_description()
self.hwid = self.usb_info()
def __eq__(self, other):
return isinstance(other, ListPortInfo) and self.device == other.device
def __hash__(self):
return hash(self.device)
def __lt__(self, other):
if not isinstance(other, ListPortInfo):
raise TypeError('unorderable types: {}() and {}()'.format(
type(self).__name__,
type(other).__name__))
return numsplit(self.device) < numsplit(other.device)
def __str__(self):
return '{} - {}'.format(self.device, self.description)
def __getitem__(self, index):
"""Item access: backwards compatible -> (port, desc, hwid)"""
if index == 0:
return self.device
elif index == 1:
return self.description
elif index == 2:
return self.hwid
else:
raise IndexError('{} > 2'.format(index))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def list_links(devices):
"""\
search all /dev devices and look for symlinks to known ports already
listed in devices.
"""
links = []
for device in glob.glob('/dev/*'):
if os.path.islink(device) and os.path.realpath(device) in devices:
links.append(device)
return links
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# test
if __name__ == '__main__':
print(ListPortInfo('dummy'))
-112
View File
@@ -1,112 +0,0 @@
#!/usr/bin/env python
#
# This is a module that gathers a list of serial ports including details on
# GNU/Linux systems.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import absolute_import
import glob
import os
from serial.tools import list_ports_common
class SysFS(list_ports_common.ListPortInfo):
"""Wrapper for easy sysfs access and device info"""
def __init__(self, device):
super(SysFS, self).__init__(device)
# special handling for links
if device is not None and os.path.islink(device):
device = os.path.realpath(device)
is_link = True
else:
is_link = False
self.usb_device_path = None
if os.path.exists('/sys/class/tty/{}/device'.format(self.name)):
self.device_path = os.path.realpath('/sys/class/tty/{}/device'.format(self.name))
self.subsystem = os.path.basename(os.path.realpath(os.path.join(self.device_path, 'subsystem')))
else:
self.device_path = None
self.subsystem = None
# check device type
if self.subsystem == 'usb-serial':
self.usb_interface_path = os.path.dirname(self.device_path)
elif self.subsystem == 'usb':
self.usb_interface_path = self.device_path
else:
self.usb_interface_path = None
# fill-in info for USB devices
if self.usb_interface_path is not None:
self.usb_device_path = os.path.dirname(self.usb_interface_path)
try:
num_if = int(self.read_line(self.usb_device_path, 'bNumInterfaces'))
except ValueError:
num_if = 1
self.vid = int(self.read_line(self.usb_device_path, 'idVendor'), 16)
self.pid = int(self.read_line(self.usb_device_path, 'idProduct'), 16)
self.serial_number = self.read_line(self.usb_device_path, 'serial')
if num_if > 1: # multi interface devices like FT4232
self.location = os.path.basename(self.usb_interface_path)
else:
self.location = os.path.basename(self.usb_device_path)
self.manufacturer = self.read_line(self.usb_device_path, 'manufacturer')
self.product = self.read_line(self.usb_device_path, 'product')
self.interface = self.read_line(self.usb_interface_path, 'interface')
if self.subsystem in ('usb', 'usb-serial'):
self.apply_usb_info()
#~ elif self.subsystem in ('pnp', 'amba'): # PCI based devices, raspi
elif self.subsystem == 'pnp': # PCI based devices
self.description = self.name
self.hwid = self.read_line(self.device_path, 'id')
elif self.subsystem == 'amba': # raspi
self.description = self.name
self.hwid = os.path.basename(self.device_path)
if is_link:
self.hwid += ' LINK={}'.format(device)
def read_line(self, *args):
"""\
Helper function to read a single line from a file.
One or more parameters are allowed, they are joined with os.path.join.
Returns None on errors..
"""
try:
with open(os.path.join(*args)) as f:
line = f.readline().strip()
return line
except IOError:
return None
def comports(include_links=False):
devices = set()
devices.update(glob.glob('/dev/ttyS*')) # built-in serial ports
devices.update(glob.glob('/dev/ttyUSB*')) # usb-serial with own driver
devices.update(glob.glob('/dev/ttyXRUSB*')) # xr-usb-serial port exar (DELL Edge 3001)
devices.update(glob.glob('/dev/ttyACM*')) # usb-serial with CDC-ACM profile
devices.update(glob.glob('/dev/ttyAMA*')) # ARM internal port (raspi)
devices.update(glob.glob('/dev/rfcomm*')) # BT serial devices
devices.update(glob.glob('/dev/ttyAP*')) # Advantech multi-port serial controllers
devices.update(glob.glob('/dev/ttyGS*')) # https://www.kernel.org/doc/Documentation/usb/gadget_serial.txt
if include_links:
devices.update(list_ports_common.list_links(devices))
return [info
for info in [SysFS(d) for d in devices]
if info.subsystem != "platform"] # hide non-present internal serial ports
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# test
if __name__ == '__main__':
for info in sorted(comports()):
print("{0}: {0.subsystem}".format(info))
-299
View File
@@ -1,299 +0,0 @@
#!/usr/bin/env python
#
# This is a module that gathers a list of serial ports including details on OSX
#
# code originally from https://github.com/makerbot/pyserial/tree/master/serial/tools
# with contributions from cibomahto, dgs3, FarMcKon, tedbrandston
# and modifications by cliechti, hoihu, hardkrash
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2013-2020
#
# SPDX-License-Identifier: BSD-3-Clause
# List all of the callout devices in OS/X by querying IOKit.
# See the following for a reference of how to do this:
# http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html#//apple_ref/doc/uid/TP30000384-CIHGEAFD
# More help from darwin_hid.py
# Also see the 'IORegistryExplorer' for an idea of what we are actually searching
from __future__ import absolute_import
import ctypes
from serial.tools import list_ports_common
iokit = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit')
cf = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation')
# kIOMasterPortDefault is no longer exported in BigSur but no biggie, using NULL works just the same
kIOMasterPortDefault = 0 # WAS: ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault")
kCFAllocatorDefault = ctypes.c_void_p.in_dll(cf, "kCFAllocatorDefault")
kCFStringEncodingMacRoman = 0
kCFStringEncodingUTF8 = 0x08000100
# defined in `IOKit/usb/USBSpec.h`
kUSBVendorString = 'USB Vendor Name'
kUSBSerialNumberString = 'USB Serial Number'
# `io_name_t` defined as `typedef char io_name_t[128];`
# in `device/device_types.h`
io_name_size = 128
# defined in `mach/kern_return.h`
KERN_SUCCESS = 0
# kern_return_t defined as `typedef int kern_return_t;` in `mach/i386/kern_return.h`
kern_return_t = ctypes.c_int
iokit.IOServiceMatching.restype = ctypes.c_void_p
iokit.IOServiceGetMatchingServices.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
iokit.IOServiceGetMatchingServices.restype = kern_return_t
iokit.IORegistryEntryGetParentEntry.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
iokit.IOServiceGetMatchingServices.restype = kern_return_t
iokit.IORegistryEntryCreateCFProperty.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32]
iokit.IORegistryEntryCreateCFProperty.restype = ctypes.c_void_p
iokit.IORegistryEntryGetPath.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
iokit.IORegistryEntryGetPath.restype = kern_return_t
iokit.IORegistryEntryGetName.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
iokit.IORegistryEntryGetName.restype = kern_return_t
iokit.IOObjectGetClass.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
iokit.IOObjectGetClass.restype = kern_return_t
iokit.IOObjectRelease.argtypes = [ctypes.c_void_p]
cf.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int32]
cf.CFStringCreateWithCString.restype = ctypes.c_void_p
cf.CFStringGetCStringPtr.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
cf.CFStringGetCStringPtr.restype = ctypes.c_char_p
cf.CFStringGetCString.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_long, ctypes.c_uint32]
cf.CFStringGetCString.restype = ctypes.c_bool
cf.CFNumberGetValue.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p]
cf.CFNumberGetValue.restype = ctypes.c_void_p
# void CFRelease ( CFTypeRef cf );
cf.CFRelease.argtypes = [ctypes.c_void_p]
cf.CFRelease.restype = None
# CFNumber type defines
kCFNumberSInt8Type = 1
kCFNumberSInt16Type = 2
kCFNumberSInt32Type = 3
kCFNumberSInt64Type = 4
def get_string_property(device_type, property):
"""
Search the given device for the specified string property
@param device_type Type of Device
@param property String to search for
@return Python string containing the value, or None if not found.
"""
key = cf.CFStringCreateWithCString(
kCFAllocatorDefault,
property.encode("utf-8"),
kCFStringEncodingUTF8)
CFContainer = iokit.IORegistryEntryCreateCFProperty(
device_type,
key,
kCFAllocatorDefault,
0)
output = None
if CFContainer:
output = cf.CFStringGetCStringPtr(CFContainer, 0)
if output is not None:
output = output.decode('utf-8')
else:
buffer = ctypes.create_string_buffer(io_name_size);
success = cf.CFStringGetCString(CFContainer, ctypes.byref(buffer), io_name_size, kCFStringEncodingUTF8)
if success:
output = buffer.value.decode('utf-8')
cf.CFRelease(CFContainer)
return output
def get_int_property(device_type, property, cf_number_type):
"""
Search the given device for the specified string property
@param device_type Device to search
@param property String to search for
@param cf_number_type CFType number
@return Python string containing the value, or None if not found.
"""
key = cf.CFStringCreateWithCString(
kCFAllocatorDefault,
property.encode("utf-8"),
kCFStringEncodingUTF8)
CFContainer = iokit.IORegistryEntryCreateCFProperty(
device_type,
key,
kCFAllocatorDefault,
0)
if CFContainer:
if (cf_number_type == kCFNumberSInt32Type):
number = ctypes.c_uint32()
elif (cf_number_type == kCFNumberSInt16Type):
number = ctypes.c_uint16()
cf.CFNumberGetValue(CFContainer, cf_number_type, ctypes.byref(number))
cf.CFRelease(CFContainer)
return number.value
return None
def IORegistryEntryGetName(device):
devicename = ctypes.create_string_buffer(io_name_size);
res = iokit.IORegistryEntryGetName(device, ctypes.byref(devicename))
if res != KERN_SUCCESS:
return None
# this works in python2 but may not be valid. Also I don't know if
# this encoding is guaranteed. It may be dependent on system locale.
return devicename.value.decode('utf-8')
def IOObjectGetClass(device):
classname = ctypes.create_string_buffer(io_name_size)
iokit.IOObjectGetClass(device, ctypes.byref(classname))
return classname.value
def GetParentDeviceByType(device, parent_type):
""" Find the first parent of a device that implements the parent_type
@param IOService Service to inspect
@return Pointer to the parent type, or None if it was not found.
"""
# First, try to walk up the IOService tree to find a parent of this device that is a IOUSBDevice.
parent_type = parent_type.encode('utf-8')
while IOObjectGetClass(device) != parent_type:
parent = ctypes.c_void_p()
response = iokit.IORegistryEntryGetParentEntry(
device,
"IOService".encode("utf-8"),
ctypes.byref(parent))
# If we weren't able to find a parent for the device, we're done.
if response != KERN_SUCCESS:
return None
device = parent
return device
def GetIOServicesByType(service_type):
"""
returns iterator over specified service_type
"""
serial_port_iterator = ctypes.c_void_p()
iokit.IOServiceGetMatchingServices(
kIOMasterPortDefault,
iokit.IOServiceMatching(service_type.encode('utf-8')),
ctypes.byref(serial_port_iterator))
services = []
while iokit.IOIteratorIsValid(serial_port_iterator):
service = iokit.IOIteratorNext(serial_port_iterator)
if not service:
break
services.append(service)
iokit.IOObjectRelease(serial_port_iterator)
return services
def location_to_string(locationID):
"""
helper to calculate port and bus number from locationID
"""
loc = ['{}-'.format(locationID >> 24)]
while locationID & 0xf00000:
if len(loc) > 1:
loc.append('.')
loc.append('{}'.format((locationID >> 20) & 0xf))
locationID <<= 4
return ''.join(loc)
class SuitableSerialInterface(object):
pass
def scan_interfaces():
"""
helper function to scan USB interfaces
returns a list of SuitableSerialInterface objects with name and id attributes
"""
interfaces = []
for service in GetIOServicesByType('IOSerialBSDClient'):
device = get_string_property(service, "IOCalloutDevice")
if device:
usb_device = GetParentDeviceByType(service, "IOUSBInterface")
if usb_device:
name = get_string_property(usb_device, "USB Interface Name") or None
locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) or ''
i = SuitableSerialInterface()
i.id = locationID
i.name = name
interfaces.append(i)
return interfaces
def search_for_locationID_in_interfaces(serial_interfaces, locationID):
for interface in serial_interfaces:
if (interface.id == locationID):
return interface.name
return None
def comports(include_links=False):
# XXX include_links is currently ignored. are links in /dev even supported here?
# Scan for all iokit serial ports
services = GetIOServicesByType('IOSerialBSDClient')
ports = []
serial_interfaces = scan_interfaces()
for service in services:
# First, add the callout device file.
device = get_string_property(service, "IOCalloutDevice")
if device:
info = list_ports_common.ListPortInfo(device)
# If the serial port is implemented by IOUSBDevice
# NOTE IOUSBDevice was deprecated as of 10.11 and finally on Apple Silicon
# devices has been completely removed. Thanks to @oskay for this patch.
usb_device = GetParentDeviceByType(service, "IOUSBHostDevice")
if not usb_device:
usb_device = GetParentDeviceByType(service, "IOUSBDevice")
if usb_device:
# fetch some useful information from properties
info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type)
info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type)
info.serial_number = get_string_property(usb_device, kUSBSerialNumberString)
# We know this is a usb device, so the
# IORegistryEntryName should always be aliased to the
# usb product name string descriptor.
info.product = IORegistryEntryGetName(usb_device) or 'n/a'
info.manufacturer = get_string_property(usb_device, kUSBVendorString)
locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type)
info.location = location_to_string(locationID)
info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID)
info.apply_usb_info()
ports.append(info)
return ports
# test
if __name__ == '__main__':
for port, desc, hwid in sorted(comports()):
print("{}: {} [{}]".format(port, desc, hwid))
-119
View File
@@ -1,119 +0,0 @@
#!/usr/bin/env python
#
# This is a module that gathers a list of serial ports on POSIXy systems.
# For some specific implementations, see also list_ports_linux, list_ports_osx
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
"""\
The ``comports`` function is expected to return an iterable that yields tuples
of 3 strings: port name, human readable description and a hardware ID.
As currently no method is known to get the second two strings easily, they are
currently just identical to the port name.
"""
from __future__ import absolute_import
import glob
import sys
import os
from serial.tools import list_ports_common
# try to detect the OS so that a device can be selected...
plat = sys.platform.lower()
if plat[:5] == 'linux': # Linux (confirmed) # noqa
from serial.tools.list_ports_linux import comports
elif plat[:6] == 'darwin': # OS X (confirmed)
from serial.tools.list_ports_osx import comports
elif plat == 'cygwin': # cygwin/win32
# cygwin accepts /dev/com* in many contexts
# (such as 'open' call, explicit 'ls'), but 'glob.glob'
# and bare 'ls' do not; so use /dev/ttyS* instead
def comports(include_links=False):
devices = set(glob.glob('/dev/ttyS*'))
if include_links:
devices.update(list_ports_common.list_links(devices))
return [list_ports_common.ListPortInfo(d) for d in devices]
elif plat[:7] == 'openbsd': # OpenBSD
def comports(include_links=False):
devices = set(glob.glob('/dev/cua*'))
if include_links:
devices.update(list_ports_common.list_links(devices))
return [list_ports_common.ListPortInfo(d) for d in devices]
elif plat[:3] == 'bsd' or plat[:7] == 'freebsd':
def comports(include_links=False):
devices = set(glob.glob('/dev/cua*[!.init][!.lock]'))
if include_links:
devices.update(list_ports_common.list_links(devices))
return [list_ports_common.ListPortInfo(d) for d in devices]
elif plat[:6] == 'netbsd': # NetBSD
def comports(include_links=False):
"""scan for available ports. return a list of device names."""
devices = set(glob.glob('/dev/dty*'))
if include_links:
devices.update(list_ports_common.list_links(devices))
return [list_ports_common.ListPortInfo(d) for d in devices]
elif plat[:4] == 'irix': # IRIX
def comports(include_links=False):
"""scan for available ports. return a list of device names."""
devices = set(glob.glob('/dev/ttyf*'))
if include_links:
devices.update(list_ports_common.list_links(devices))
return [list_ports_common.ListPortInfo(d) for d in devices]
elif plat[:2] == 'hp': # HP-UX (not tested)
def comports(include_links=False):
"""scan for available ports. return a list of device names."""
devices = set(glob.glob('/dev/tty*p0'))
if include_links:
devices.update(list_ports_common.list_links(devices))
return [list_ports_common.ListPortInfo(d) for d in devices]
elif plat[:5] == 'sunos': # Solaris/SunOS
def comports(include_links=False):
"""scan for available ports. return a list of device names."""
devices = set(glob.glob('/dev/tty*c'))
if include_links:
devices.update(list_ports_common.list_links(devices))
return [list_ports_common.ListPortInfo(d) for d in devices]
elif plat[:3] == 'aix': # AIX
def comports(include_links=False):
"""scan for available ports. return a list of device names."""
devices = set(glob.glob('/dev/tty*'))
if include_links:
devices.update(list_ports_common.list_links(devices))
return [list_ports_common.ListPortInfo(d) for d in devices]
else:
# platform detection has failed...
import serial
sys.stderr.write("""\
don't know how to enumerate ttys on this system.
! I you know how the serial ports are named send this information to
! the author of this module:
sys.platform = {!r}
os.name = {!r}
pySerial version = {}
also add the naming scheme of the serial ports and with a bit luck you can get
this module running...
""".format(sys.platform, os.name, serial.VERSION))
raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name))
# test
if __name__ == '__main__':
for port, desc, hwid in sorted(comports()):
print("{}: {} [{}]".format(port, desc, hwid))
-427
View File
@@ -1,427 +0,0 @@
#! python
#
# Enumerate serial ports on Windows including a human readable description
# and hardware information.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2001-2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import absolute_import
# pylint: disable=invalid-name,too-few-public-methods
import re
import ctypes
from ctypes.wintypes import BOOL
from ctypes.wintypes import HWND
from ctypes.wintypes import DWORD
from ctypes.wintypes import WORD
from ctypes.wintypes import LONG
from ctypes.wintypes import ULONG
from ctypes.wintypes import HKEY
from ctypes.wintypes import BYTE
import serial
from serial.win32 import ULONG_PTR
from serial.tools import list_ports_common
def ValidHandle(value, func, arguments):
if value == 0:
raise ctypes.WinError()
return value
NULL = 0
HDEVINFO = ctypes.c_void_p
LPCTSTR = ctypes.c_wchar_p
PCTSTR = ctypes.c_wchar_p
PTSTR = ctypes.c_wchar_p
LPDWORD = PDWORD = ctypes.POINTER(DWORD)
#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE)
LPBYTE = PBYTE = ctypes.c_void_p # XXX avoids error about types
ACCESS_MASK = DWORD
REGSAM = ACCESS_MASK
class GUID(ctypes.Structure):
_fields_ = [
('Data1', DWORD),
('Data2', WORD),
('Data3', WORD),
('Data4', BYTE * 8),
]
def __str__(self):
return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format(
self.Data1,
self.Data2,
self.Data3,
''.join(["{:02x}".format(d) for d in self.Data4[:2]]),
''.join(["{:02x}".format(d) for d in self.Data4[2:]]),
)
class SP_DEVINFO_DATA(ctypes.Structure):
_fields_ = [
('cbSize', DWORD),
('ClassGuid', GUID),
('DevInst', DWORD),
('Reserved', ULONG_PTR),
]
def __str__(self):
return "ClassGuid:{} DevInst:{}".format(self.ClassGuid, self.DevInst)
PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
setupapi = ctypes.windll.LoadLibrary("setupapi")
SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
SetupDiDestroyDeviceInfoList.restype = BOOL
SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameW
SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD]
SetupDiClassGuidsFromName.restype = BOOL
SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA]
SetupDiEnumDeviceInfo.restype = BOOL
SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW
SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
SetupDiGetClassDevs.restype = HDEVINFO
SetupDiGetClassDevs.errcheck = ValidHandle
SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyW
SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD]
SetupDiGetDeviceRegistryProperty.restype = BOOL
SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdW
SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD]
SetupDiGetDeviceInstanceId.restype = BOOL
SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey
SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM]
SetupDiOpenDevRegKey.restype = HKEY
advapi32 = ctypes.windll.LoadLibrary("Advapi32")
RegCloseKey = advapi32.RegCloseKey
RegCloseKey.argtypes = [HKEY]
RegCloseKey.restype = LONG
RegQueryValueEx = advapi32.RegQueryValueExW
RegQueryValueEx.argtypes = [HKEY, LPCTSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD]
RegQueryValueEx.restype = LONG
cfgmgr32 = ctypes.windll.LoadLibrary("Cfgmgr32")
CM_Get_Parent = cfgmgr32.CM_Get_Parent
CM_Get_Parent.argtypes = [PDWORD, DWORD, ULONG]
CM_Get_Parent.restype = LONG
CM_Get_Device_IDW = cfgmgr32.CM_Get_Device_IDW
CM_Get_Device_IDW.argtypes = [DWORD, PTSTR, ULONG, ULONG]
CM_Get_Device_IDW.restype = LONG
CM_MapCrToWin32Err = cfgmgr32.CM_MapCrToWin32Err
CM_MapCrToWin32Err.argtypes = [DWORD, DWORD]
CM_MapCrToWin32Err.restype = DWORD
DIGCF_PRESENT = 2
DIGCF_DEVICEINTERFACE = 16
INVALID_HANDLE_VALUE = 0
ERROR_INSUFFICIENT_BUFFER = 122
ERROR_NOT_FOUND = 1168
SPDRP_HARDWAREID = 1
SPDRP_FRIENDLYNAME = 12
SPDRP_LOCATION_PATHS = 35
SPDRP_MFG = 11
DICS_FLAG_GLOBAL = 1
DIREG_DEV = 0x00000001
KEY_READ = 0x20019
MAX_USB_DEVICE_TREE_TRAVERSAL_DEPTH = 5
def get_parent_serial_number(child_devinst, child_vid, child_pid, depth=0, last_serial_number=None):
""" Get the serial number of the parent of a device.
Args:
child_devinst: The device instance handle to get the parent serial number of.
child_vid: The vendor ID of the child device.
child_pid: The product ID of the child device.
depth: The current iteration depth of the USB device tree.
"""
# If the traversal depth is beyond the max, abandon attempting to find the serial number.
if depth > MAX_USB_DEVICE_TREE_TRAVERSAL_DEPTH:
return '' if not last_serial_number else last_serial_number
# Get the parent device instance.
devinst = DWORD()
ret = CM_Get_Parent(ctypes.byref(devinst), child_devinst, 0)
if ret:
win_error = CM_MapCrToWin32Err(DWORD(ret), DWORD(0))
# If there is no parent available, the child was the root device. We cannot traverse
# further.
if win_error == ERROR_NOT_FOUND:
return '' if not last_serial_number else last_serial_number
raise ctypes.WinError(win_error)
# Get the ID of the parent device and parse it for vendor ID, product ID, and serial number.
parentHardwareID = ctypes.create_unicode_buffer(250)
ret = CM_Get_Device_IDW(
devinst,
parentHardwareID,
ctypes.sizeof(parentHardwareID) - 1,
0)
if ret:
raise ctypes.WinError(CM_MapCrToWin32Err(DWORD(ret), DWORD(0)))
parentHardwareID_str = parentHardwareID.value
m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(.*))?',
parentHardwareID_str,
re.I)
# return early if we have no matches (likely malformed serial, traversed too far)
if not m:
return '' if not last_serial_number else last_serial_number
vid = None
pid = None
serial_number = None
if m.group(1):
vid = int(m.group(1), 16)
if m.group(3):
pid = int(m.group(3), 16)
if m.group(7):
serial_number = m.group(7)
# store what we found as a fallback for malformed serial values up the chain
found_serial_number = serial_number
# Check that the USB serial number only contains alphanumeric characters. It may be a windows
# device ID (ephemeral ID).
if serial_number and not re.match(r'^\w+$', serial_number):
serial_number = None
if not vid or not pid:
# If pid and vid are not available at this device level, continue to the parent.
return get_parent_serial_number(devinst, child_vid, child_pid, depth + 1, found_serial_number)
if pid != child_pid or vid != child_vid:
# If the VID or PID has changed, we are no longer looking at the same physical device. The
# serial number is unknown.
return '' if not last_serial_number else last_serial_number
# In this case, the vid and pid of the parent device are identical to the child. However, if
# there still isn't a serial number available, continue to the next parent.
if not serial_number:
return get_parent_serial_number(devinst, child_vid, child_pid, depth + 1, found_serial_number)
# Finally, the VID and PID are identical to the child and a serial number is present, so return
# it.
return serial_number
def iterate_comports():
"""Return a generator that yields descriptions for serial ports"""
PortsGUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough...
ports_guids_size = DWORD()
if not SetupDiClassGuidsFromName(
"Ports",
PortsGUIDs,
ctypes.sizeof(PortsGUIDs),
ctypes.byref(ports_guids_size)):
raise ctypes.WinError()
ModemsGUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough...
modems_guids_size = DWORD()
if not SetupDiClassGuidsFromName(
"Modem",
ModemsGUIDs,
ctypes.sizeof(ModemsGUIDs),
ctypes.byref(modems_guids_size)):
raise ctypes.WinError()
GUIDs = PortsGUIDs[:ports_guids_size.value] + ModemsGUIDs[:modems_guids_size.value]
# repeat for all possible GUIDs
for index in range(len(GUIDs)):
bInterfaceNumber = None
g_hdi = SetupDiGetClassDevs(
ctypes.byref(GUIDs[index]),
None,
NULL,
DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports
devinfo = SP_DEVINFO_DATA()
devinfo.cbSize = ctypes.sizeof(devinfo)
index = 0
while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
index += 1
# get the real com port name
hkey = SetupDiOpenDevRegKey(
g_hdi,
ctypes.byref(devinfo),
DICS_FLAG_GLOBAL,
0,
DIREG_DEV, # DIREG_DRV for SW info
KEY_READ)
port_name_buffer = ctypes.create_unicode_buffer(250)
port_name_length = ULONG(ctypes.sizeof(port_name_buffer))
RegQueryValueEx(
hkey,
"PortName",
None,
None,
ctypes.byref(port_name_buffer),
ctypes.byref(port_name_length))
RegCloseKey(hkey)
# unfortunately does this method also include parallel ports.
# we could check for names starting with COM or just exclude LPT
# and hope that other "unknown" names are serial ports...
if port_name_buffer.value.startswith('LPT'):
continue
# hardware ID
szHardwareID = ctypes.create_unicode_buffer(250)
# try to get ID that includes serial number
if not SetupDiGetDeviceInstanceId(
g_hdi,
ctypes.byref(devinfo),
#~ ctypes.byref(szHardwareID),
szHardwareID,
ctypes.sizeof(szHardwareID) - 1,
None):
# fall back to more generic hardware ID if that would fail
if not SetupDiGetDeviceRegistryProperty(
g_hdi,
ctypes.byref(devinfo),
SPDRP_HARDWAREID,
None,
ctypes.byref(szHardwareID),
ctypes.sizeof(szHardwareID) - 1,
None):
# Ignore ERROR_INSUFFICIENT_BUFFER
if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
raise ctypes.WinError()
# stringify
szHardwareID_str = szHardwareID.value
info = list_ports_common.ListPortInfo(port_name_buffer.value, skip_link_detection=True)
# in case of USB, make a more readable string, similar to that form
# that we also generate on other platforms
if szHardwareID_str.startswith('USB'):
m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(.*))?', szHardwareID_str, re.I)
if m:
info.vid = int(m.group(1), 16)
if m.group(3):
info.pid = int(m.group(3), 16)
if m.group(5):
bInterfaceNumber = int(m.group(5))
# Check that the USB serial number only contains alphanumeric characters. It
# may be a windows device ID (ephemeral ID) for composite devices.
if m.group(7) and re.match(r'^\w+$', m.group(7)):
info.serial_number = m.group(7)
else:
info.serial_number = get_parent_serial_number(devinfo.DevInst, info.vid, info.pid)
# calculate a location string
loc_path_str = ctypes.create_unicode_buffer(500)
if SetupDiGetDeviceRegistryProperty(
g_hdi,
ctypes.byref(devinfo),
SPDRP_LOCATION_PATHS,
None,
ctypes.byref(loc_path_str),
ctypes.sizeof(loc_path_str) - 1,
None):
m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', loc_path_str.value)
location = []
for g in m:
if g.group(1):
location.append('{:d}'.format(int(g.group(1)) + 1))
else:
if len(location) > 1:
location.append('.')
else:
location.append('-')
location.append(g.group(2))
if bInterfaceNumber is not None:
location.append(':{}.{}'.format(
'x', # XXX how to determine correct bConfigurationValue?
bInterfaceNumber))
if location:
info.location = ''.join(location)
info.hwid = info.usb_info()
elif szHardwareID_str.startswith('FTDIBUS'):
m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w+))?', szHardwareID_str, re.I)
if m:
info.vid = int(m.group(1), 16)
info.pid = int(m.group(2), 16)
if m.group(4):
info.serial_number = m.group(4)
# USB location is hidden by FDTI driver :(
info.hwid = info.usb_info()
else:
info.hwid = szHardwareID_str
# friendly name
szFriendlyName = ctypes.create_unicode_buffer(250)
if SetupDiGetDeviceRegistryProperty(
g_hdi,
ctypes.byref(devinfo),
SPDRP_FRIENDLYNAME,
#~ SPDRP_DEVICEDESC,
None,
ctypes.byref(szFriendlyName),
ctypes.sizeof(szFriendlyName) - 1,
None):
info.description = szFriendlyName.value
#~ else:
# Ignore ERROR_INSUFFICIENT_BUFFER
#~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
#~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value))
# ignore errors and still include the port in the list, friendly name will be same as port name
# manufacturer
szManufacturer = ctypes.create_unicode_buffer(250)
if SetupDiGetDeviceRegistryProperty(
g_hdi,
ctypes.byref(devinfo),
SPDRP_MFG,
#~ SPDRP_DEVICEDESC,
None,
ctypes.byref(szManufacturer),
ctypes.sizeof(szManufacturer) - 1,
None):
info.manufacturer = szManufacturer.value
yield info
SetupDiDestroyDeviceInfoList(g_hdi)
def comports(include_links=False):
"""Return a list of info objects about serial ports"""
return list(iterate_comports())
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# test
if __name__ == '__main__':
for port, desc, hwid in sorted(comports()):
print("{}: {} [{}]".format(port, desc, hwid))
File diff suppressed because it is too large Load Diff
View File
-57
View File
@@ -1,57 +0,0 @@
#! python
#
# This module implements a special URL handler that allows selecting an
# alternate implementation provided by some backends.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
#
# URL format: alt://port[?option[=value][&option[=value]]]
# options:
# - class=X used class named X instead of Serial
#
# example:
# use poll based implementation on Posix (Linux):
# python -m serial.tools.miniterm alt:///dev/ttyUSB0?class=PosixPollSerial
from __future__ import absolute_import
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
import serial
def serial_class_for_url(url):
"""extract host and port from an URL string"""
parts = urlparse.urlsplit(url)
if parts.scheme != 'alt':
raise serial.SerialException(
'expected a string in the form "alt://port[?option[=value][&option[=value]]]": '
'not starting with alt:// ({!r})'.format(parts.scheme))
class_name = 'Serial'
try:
for option, values in urlparse.parse_qs(parts.query, True).items():
if option == 'class':
class_name = values[0]
else:
raise ValueError('unknown option: {!r}'.format(option))
except ValueError as e:
raise serial.SerialException(
'expected a string in the form '
'"alt://port[?option[=value][&option[=value]]]": {!r}'.format(e))
if not hasattr(serial, class_name):
raise ValueError('unknown class: {!r}'.format(class_name))
cls = getattr(serial, class_name)
if not issubclass(cls, serial.Serial):
raise ValueError('class {!r} is not an instance of Serial'.format(class_name))
return (''.join([parts.netloc, parts.path]), cls)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if __name__ == '__main__':
s = serial.serial_for_url('alt:///dev/ttyS0?class=PosixPollSerial')
print(s)
-258
View File
@@ -1,258 +0,0 @@
#! python
#
# Backend for Silicon Labs CP2110/4 HID-to-UART devices.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
# (C) 2019 Google LLC
#
# SPDX-License-Identifier: BSD-3-Clause
# This backend implements support for HID-to-UART devices manufactured
# by Silicon Labs and marketed as CP2110 and CP2114. The
# implementation is (mostly) OS-independent and in userland. It relies
# on cython-hidapi (https://github.com/trezor/cython-hidapi).
# The HID-to-UART protocol implemented by CP2110/4 is described in the
# AN434 document from Silicon Labs:
# https://www.silabs.com/documents/public/application-notes/AN434-CP2110-4-Interface-Specification.pdf
# TODO items:
# - rtscts support is configured for hardware flow control, but the
# signaling is missing (AN434 suggests this is done through GPIO).
# - Cancelling reads and writes is not supported.
# - Baudrate validation is not implemented, as it depends on model and configuration.
import struct
import threading
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
try:
import Queue
except ImportError:
import queue as Queue
import hid # hidapi
import serial
from serial.serialutil import SerialBase, SerialException, PortNotOpenError, to_bytes, Timeout
# Report IDs and related constant
_REPORT_GETSET_UART_ENABLE = 0x41
_DISABLE_UART = 0x00
_ENABLE_UART = 0x01
_REPORT_SET_PURGE_FIFOS = 0x43
_PURGE_TX_FIFO = 0x01
_PURGE_RX_FIFO = 0x02
_REPORT_GETSET_UART_CONFIG = 0x50
_REPORT_SET_TRANSMIT_LINE_BREAK = 0x51
_REPORT_SET_STOP_LINE_BREAK = 0x52
class Serial(SerialBase):
# This is not quite correct. AN343 specifies that the minimum
# baudrate is different between CP2110 and CP2114, and it's halved
# when using non-8-bit symbols.
BAUDRATES = (300, 375, 600, 1200, 1800, 2400, 4800, 9600, 19200,
38400, 57600, 115200, 230400, 460800, 500000, 576000,
921600, 1000000)
def __init__(self, *args, **kwargs):
self._hid_handle = None
self._read_buffer = None
self._thread = None
super(Serial, self).__init__(*args, **kwargs)
def open(self):
if self._port is None:
raise SerialException("Port must be configured before it can be used.")
if self.is_open:
raise SerialException("Port is already open.")
self._read_buffer = Queue.Queue()
self._hid_handle = hid.device()
try:
portpath = self.from_url(self.portstr)
self._hid_handle.open_path(portpath)
except OSError as msg:
raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg))
try:
self._reconfigure_port()
except:
try:
self._hid_handle.close()
except:
pass
self._hid_handle = None
raise
else:
self.is_open = True
self._thread = threading.Thread(target=self._hid_read_loop)
self._thread.daemon = True
self._thread.setName('pySerial CP2110 reader thread for {}'.format(self._port))
self._thread.start()
def from_url(self, url):
parts = urlparse.urlsplit(url)
if parts.scheme != "cp2110":
raise SerialException(
'expected a string in the forms '
'"cp2110:///dev/hidraw9" or "cp2110://0001:0023:00": '
'not starting with cp2110:// {{!r}}'.format(parts.scheme))
if parts.netloc: # cp2100://BUS:DEVICE:ENDPOINT, for libusb
return parts.netloc.encode('utf-8')
return parts.path.encode('utf-8')
def close(self):
self.is_open = False
if self._thread:
self._thread.join(1) # read timeout is 0.1
self._thread = None
self._hid_handle.close()
self._hid_handle = None
def _reconfigure_port(self):
parity_value = None
if self._parity == serial.PARITY_NONE:
parity_value = 0x00
elif self._parity == serial.PARITY_ODD:
parity_value = 0x01
elif self._parity == serial.PARITY_EVEN:
parity_value = 0x02
elif self._parity == serial.PARITY_MARK:
parity_value = 0x03
elif self._parity == serial.PARITY_SPACE:
parity_value = 0x04
else:
raise ValueError('Invalid parity: {!r}'.format(self._parity))
if self.rtscts:
flow_control_value = 0x01
else:
flow_control_value = 0x00
data_bits_value = None
if self._bytesize == 5:
data_bits_value = 0x00
elif self._bytesize == 6:
data_bits_value = 0x01
elif self._bytesize == 7:
data_bits_value = 0x02
elif self._bytesize == 8:
data_bits_value = 0x03
else:
raise ValueError('Invalid char len: {!r}'.format(self._bytesize))
stop_bits_value = None
if self._stopbits == serial.STOPBITS_ONE:
stop_bits_value = 0x00
elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE:
stop_bits_value = 0x01
elif self._stopbits == serial.STOPBITS_TWO:
stop_bits_value = 0x01
else:
raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits))
configuration_report = struct.pack(
'>BLBBBB',
_REPORT_GETSET_UART_CONFIG,
self._baudrate,
parity_value,
flow_control_value,
data_bits_value,
stop_bits_value)
self._hid_handle.send_feature_report(configuration_report)
self._hid_handle.send_feature_report(
bytes((_REPORT_GETSET_UART_ENABLE, _ENABLE_UART)))
self._update_break_state()
@property
def in_waiting(self):
return self._read_buffer.qsize()
def reset_input_buffer(self):
if not self.is_open:
raise PortNotOpenError()
self._hid_handle.send_feature_report(
bytes((_REPORT_SET_PURGE_FIFOS, _PURGE_RX_FIFO)))
# empty read buffer
while self._read_buffer.qsize():
self._read_buffer.get(False)
def reset_output_buffer(self):
if not self.is_open:
raise PortNotOpenError()
self._hid_handle.send_feature_report(
bytes((_REPORT_SET_PURGE_FIFOS, _PURGE_TX_FIFO)))
def _update_break_state(self):
if not self._hid_handle:
raise PortNotOpenError()
if self._break_state:
self._hid_handle.send_feature_report(
bytes((_REPORT_SET_TRANSMIT_LINE_BREAK, 0)))
else:
# Note that while AN434 states "There are no data bytes in
# the payload other than the Report ID", either hidapi or
# Linux does not seem to send the report otherwise.
self._hid_handle.send_feature_report(
bytes((_REPORT_SET_STOP_LINE_BREAK, 0)))
def read(self, size=1):
if not self.is_open:
raise PortNotOpenError()
data = bytearray()
try:
timeout = Timeout(self._timeout)
while len(data) < size:
if self._thread is None:
raise SerialException('connection failed (reader thread died)')
buf = self._read_buffer.get(True, timeout.time_left())
if buf is None:
return bytes(data)
data += buf
if timeout.expired():
break
except Queue.Empty: # -> timeout
pass
return bytes(data)
def write(self, data):
if not self.is_open:
raise PortNotOpenError()
data = to_bytes(data)
tx_len = len(data)
while tx_len > 0:
to_be_sent = min(tx_len, 0x3F)
report = to_bytes([to_be_sent]) + data[:to_be_sent]
self._hid_handle.write(report)
data = data[to_be_sent:]
tx_len = len(data)
def _hid_read_loop(self):
try:
while self.is_open:
data = self._hid_handle.read(64, timeout_ms=100)
if not data:
continue
data_len = data.pop(0)
assert data_len == len(data)
self._read_buffer.put(bytearray(data))
finally:
self._thread = None
-91
View File
@@ -1,91 +0,0 @@
#! python
#
# This module implements a special URL handler that uses the port listing to
# find ports by searching the string descriptions.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
#
# URL format: hwgrep://<regexp>&<option>
#
# where <regexp> is a Python regexp according to the re module
#
# violating the normal definition for URLs, the character `&` is used to
# separate parameters from the arguments (instead of `?`, but the question mark
# is heavily used in regexp'es)
#
# options:
# n=<N> pick the N'th entry instead of the first one (numbering starts at 1)
# skip_busy tries to open port to check if it is busy, fails on posix as ports are not locked!
from __future__ import absolute_import
import serial
import serial.tools.list_ports
try:
basestring
except NameError:
basestring = str # python 3 pylint: disable=redefined-builtin
class Serial(serial.Serial):
"""Just inherit the native Serial port implementation and patch the port property."""
# pylint: disable=no-member
@serial.Serial.port.setter
def port(self, value):
"""translate port name before storing it"""
if isinstance(value, basestring) and value.startswith('hwgrep://'):
serial.Serial.port.__set__(self, self.from_url(value))
else:
serial.Serial.port.__set__(self, value)
def from_url(self, url):
"""extract host and port from an URL string"""
if url.lower().startswith("hwgrep://"):
url = url[9:]
n = 0
test_open = False
args = url.split('&')
regexp = args.pop(0)
for arg in args:
if '=' in arg:
option, value = arg.split('=', 1)
else:
option = arg
value = None
if option == 'n':
# pick n'th element
n = int(value) - 1
if n < 1:
raise ValueError('option "n" expects a positive integer larger than 1: {!r}'.format(value))
elif option == 'skip_busy':
# open to test if port is available. not the nicest way..
test_open = True
else:
raise ValueError('unknown option: {!r}'.format(option))
# use a for loop to get the 1st element from the generator
for port, desc, hwid in sorted(serial.tools.list_ports.grep(regexp)):
if test_open:
try:
s = serial.Serial(port)
except serial.SerialException:
# it has some error, skip this one
continue
else:
s.close()
if n:
n -= 1
continue
return port
else:
raise serial.SerialException('no ports found matching regexp {!r}'.format(url))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if __name__ == '__main__':
s = Serial(None)
s.port = 'hwgrep://ttyS0'
print(s)
-308
View File
@@ -1,308 +0,0 @@
#! python
#
# This module implements a loop back connection receiving itself what it sent.
#
# The purpose of this module is.. well... You can run the unit tests with it.
# and it was so easy to implement ;-)
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2001-2020 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
#
# URL format: loop://[option[/option...]]
# options:
# - "debug" print diagnostic messages
from __future__ import absolute_import
import logging
import numbers
import time
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
try:
import queue
except ImportError:
import Queue as queue
from serial.serialutil import SerialBase, SerialException, to_bytes, iterbytes, SerialTimeoutException, PortNotOpenError
# map log level names to constants. used in from_url()
LOGGER_LEVELS = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
}
class Serial(SerialBase):
"""Serial port implementation that simulates a loop back connection in plain software."""
BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200)
def __init__(self, *args, **kwargs):
self.buffer_size = 4096
self.queue = None
self.logger = None
self._cancel_write = False
super(Serial, self).__init__(*args, **kwargs)
def open(self):
"""\
Open port with current settings. This may throw a SerialException
if the port cannot be opened.
"""
if self.is_open:
raise SerialException("Port is already open.")
self.logger = None
self.queue = queue.Queue(self.buffer_size)
if self._port is None:
raise SerialException("Port must be configured before it can be used.")
# not that there is anything to open, but the function applies the
# options found in the URL
self.from_url(self.port)
# not that there anything to configure...
self._reconfigure_port()
# all things set up get, now a clean start
self.is_open = True
if not self._dsrdtr:
self._update_dtr_state()
if not self._rtscts:
self._update_rts_state()
self.reset_input_buffer()
self.reset_output_buffer()
def close(self):
if self.is_open:
self.is_open = False
try:
self.queue.put_nowait(None)
except queue.Full:
pass
super(Serial, self).close()
def _reconfigure_port(self):
"""\
Set communication parameters on opened port. For the loop://
protocol all settings are ignored!
"""
# not that's it of any real use, but it helps in the unit tests
if not isinstance(self._baudrate, numbers.Integral) or not 0 < self._baudrate < 2 ** 32:
raise ValueError("invalid baudrate: {!r}".format(self._baudrate))
if self.logger:
self.logger.info('_reconfigure_port()')
def from_url(self, url):
"""extract host and port from an URL string"""
parts = urlparse.urlsplit(url)
if parts.scheme != "loop":
raise SerialException(
'expected a string in the form '
'"loop://[?logging={debug|info|warning|error}]": not starting '
'with loop:// ({!r})'.format(parts.scheme))
try:
# process options now, directly altering self
for option, values in urlparse.parse_qs(parts.query, True).items():
if option == 'logging':
logging.basicConfig() # XXX is that good to call it here?
self.logger = logging.getLogger('pySerial.loop')
self.logger.setLevel(LOGGER_LEVELS[values[0]])
self.logger.debug('enabled logging')
else:
raise ValueError('unknown option: {!r}'.format(option))
except ValueError as e:
raise SerialException(
'expected a string in the form '
'"loop://[?logging={debug|info|warning|error}]": {}'.format(e))
# - - - - - - - - - - - - - - - - - - - - - - - -
@property
def in_waiting(self):
"""Return the number of bytes currently in the input buffer."""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
# attention the logged value can differ from return value in
# threaded environments...
self.logger.debug('in_waiting -> {:d}'.format(self.queue.qsize()))
return self.queue.qsize()
def read(self, size=1):
"""\
Read size bytes from the serial port. If a timeout is set it may
return less characters as requested. With no timeout it will block
until the requested number of bytes is read.
"""
if not self.is_open:
raise PortNotOpenError()
if self._timeout is not None and self._timeout != 0:
timeout = time.time() + self._timeout
else:
timeout = None
data = bytearray()
while size > 0 and self.is_open:
try:
b = self.queue.get(timeout=self._timeout) # XXX inter char timeout
except queue.Empty:
if self._timeout == 0:
break
else:
if b is not None:
data += b
size -= 1
else:
break
# check for timeout now, after data has been read.
# useful for timeout = 0 (non blocking) read
if timeout and time.time() > timeout:
if self.logger:
self.logger.info('read timeout')
break
return bytes(data)
def cancel_read(self):
self.queue.put_nowait(None)
def cancel_write(self):
self._cancel_write = True
def write(self, data):
"""\
Output the given byte string over the serial port. Can block if the
connection is blocked. May raise SerialException if the connection is
closed.
"""
self._cancel_write = False
if not self.is_open:
raise PortNotOpenError()
data = to_bytes(data)
# calculate aprox time that would be used to send the data
time_used_to_send = 10.0 * len(data) / self._baudrate
# when a write timeout is configured check if we would be successful
# (not sending anything, not even the part that would have time)
if self._write_timeout is not None and time_used_to_send > self._write_timeout:
# must wait so that unit test succeeds
time_left = self._write_timeout
while time_left > 0 and not self._cancel_write:
time.sleep(min(time_left, 0.5))
time_left -= 0.5
if self._cancel_write:
return 0 # XXX
raise SerialTimeoutException('Write timeout')
for byte in iterbytes(data):
self.queue.put(byte, timeout=self._write_timeout)
return len(data)
def reset_input_buffer(self):
"""Clear input buffer, discarding all that is in the buffer."""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('reset_input_buffer()')
try:
while self.queue.qsize():
self.queue.get_nowait()
except queue.Empty:
pass
def reset_output_buffer(self):
"""\
Clear output buffer, aborting the current output and
discarding all that is in the buffer.
"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('reset_output_buffer()')
try:
while self.queue.qsize():
self.queue.get_nowait()
except queue.Empty:
pass
@property
def out_waiting(self):
"""Return how many bytes the in the outgoing buffer"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
# attention the logged value can differ from return value in
# threaded environments...
self.logger.debug('out_waiting -> {:d}'.format(self.queue.qsize()))
return self.queue.qsize()
def _update_break_state(self):
"""\
Set break: Controls TXD. When active, to transmitting is
possible.
"""
if self.logger:
self.logger.info('_update_break_state({!r})'.format(self._break_state))
def _update_rts_state(self):
"""Set terminal status line: Request To Send"""
if self.logger:
self.logger.info('_update_rts_state({!r}) -> state of CTS'.format(self._rts_state))
def _update_dtr_state(self):
"""Set terminal status line: Data Terminal Ready"""
if self.logger:
self.logger.info('_update_dtr_state({!r}) -> state of DSR'.format(self._dtr_state))
@property
def cts(self):
"""Read terminal status line: Clear To Send"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('CTS -> state of RTS ({!r})'.format(self._rts_state))
return self._rts_state
@property
def dsr(self):
"""Read terminal status line: Data Set Ready"""
if self.logger:
self.logger.info('DSR -> state of DTR ({!r})'.format(self._dtr_state))
return self._dtr_state
@property
def ri(self):
"""Read terminal status line: Ring Indicator"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for RI')
return False
@property
def cd(self):
"""Read terminal status line: Carrier Detect"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for CD')
return True
# - - - platform specific - - -
# None so far
# simple client test
if __name__ == '__main__':
import sys
s = Serial('loop://')
sys.stdout.write('{}\n'.format(s))
sys.stdout.write("write...\n")
s.write("hello\n")
s.flush()
sys.stdout.write("read: {!r}\n".format(s.read(5)))
s.close()
-12
View File
@@ -1,12 +0,0 @@
#! python
#
# This is a thin wrapper to load the rfc2217 implementation.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2011 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import absolute_import
from serial.rfc2217 import Serial # noqa
-359
View File
@@ -1,359 +0,0 @@
#! python
#
# This module implements a simple socket based client.
# It does not support changing any port parameters and will silently ignore any
# requests to do so.
#
# The purpose of this module is that applications using pySerial can connect to
# TCP/IP to serial port converters that do not support RFC 2217.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
#
# URL format: socket://<host>:<port>[/option[/option...]]
# options:
# - "debug" print diagnostic messages
from __future__ import absolute_import
import errno
import logging
import select
import socket
import time
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
from serial.serialutil import SerialBase, SerialException, to_bytes, \
PortNotOpenError, SerialTimeoutException, Timeout
# map log level names to constants. used in from_url()
LOGGER_LEVELS = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
}
POLL_TIMEOUT = 5
class Serial(SerialBase):
"""Serial port implementation for plain sockets."""
BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200)
def open(self):
"""\
Open port with current settings. This may throw a SerialException
if the port cannot be opened.
"""
self.logger = None
if self._port is None:
raise SerialException("Port must be configured before it can be used.")
if self.is_open:
raise SerialException("Port is already open.")
try:
# timeout is used for write timeout support :/ and to get an initial connection timeout
self._socket = socket.create_connection(self.from_url(self.portstr), timeout=POLL_TIMEOUT)
except Exception as msg:
self._socket = None
raise SerialException("Could not open port {}: {}".format(self.portstr, msg))
# after connecting, switch to non-blocking, we're using select
self._socket.setblocking(False)
# not that there is anything to configure...
self._reconfigure_port()
# all things set up get, now a clean start
self.is_open = True
if not self._dsrdtr:
self._update_dtr_state()
if not self._rtscts:
self._update_rts_state()
self.reset_input_buffer()
self.reset_output_buffer()
def _reconfigure_port(self):
"""\
Set communication parameters on opened port. For the socket://
protocol all settings are ignored!
"""
if self._socket is None:
raise SerialException("Can only operate on open ports")
if self.logger:
self.logger.info('ignored port configuration change')
def close(self):
"""Close port"""
if self.is_open:
if self._socket:
try:
self._socket.shutdown(socket.SHUT_RDWR)
self._socket.close()
except:
# ignore errors.
pass
self._socket = None
self.is_open = False
# in case of quick reconnects, give the server some time
time.sleep(0.3)
def from_url(self, url):
"""extract host and port from an URL string"""
parts = urlparse.urlsplit(url)
if parts.scheme != "socket":
raise SerialException(
'expected a string in the form '
'"socket://<host>:<port>[?logging={debug|info|warning|error}]": '
'not starting with socket:// ({!r})'.format(parts.scheme))
try:
# process options now, directly altering self
for option, values in urlparse.parse_qs(parts.query, True).items():
if option == 'logging':
logging.basicConfig() # XXX is that good to call it here?
self.logger = logging.getLogger('pySerial.socket')
self.logger.setLevel(LOGGER_LEVELS[values[0]])
self.logger.debug('enabled logging')
else:
raise ValueError('unknown option: {!r}'.format(option))
if not 0 <= parts.port < 65536:
raise ValueError("port not in range 0...65535")
except ValueError as e:
raise SerialException(
'expected a string in the form '
'"socket://<host>:<port>[?logging={debug|info|warning|error}]": {}'.format(e))
return (parts.hostname, parts.port)
# - - - - - - - - - - - - - - - - - - - - - - - -
@property
def in_waiting(self):
"""Return the number of bytes currently in the input buffer."""
if not self.is_open:
raise PortNotOpenError()
# Poll the socket to see if it is ready for reading.
# If ready, at least one byte will be to read.
lr, lw, lx = select.select([self._socket], [], [], 0)
return len(lr)
# select based implementation, similar to posix, but only using socket API
# to be portable, additionally handle socket timeout which is used to
# emulate write timeouts
def read(self, size=1):
"""\
Read size bytes from the serial port. If a timeout is set it may
return less characters as requested. With no timeout it will block
until the requested number of bytes is read.
"""
if not self.is_open:
raise PortNotOpenError()
read = bytearray()
timeout = Timeout(self._timeout)
while len(read) < size:
try:
ready, _, _ = select.select([self._socket], [], [], timeout.time_left())
# If select was used with a timeout, and the timeout occurs, it
# returns with empty lists -> thus abort read operation.
# For timeout == 0 (non-blocking operation) also abort when
# there is nothing to read.
if not ready:
break # timeout
buf = self._socket.recv(size - len(read))
# read should always return some data as select reported it was
# ready to read when we get to this point, unless it is EOF
if not buf:
raise SerialException('socket disconnected')
read.extend(buf)
except OSError as e:
# this is for Python 3.x where select.error is a subclass of
# OSError ignore BlockingIOErrors and EINTR. other errors are shown
# https://www.python.org/dev/peps/pep-0475.
if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('read failed: {}'.format(e))
except (select.error, socket.error) as e:
# this is for Python 2.x
# ignore BlockingIOErrors and EINTR. all errors are shown
# see also http://www.python.org/dev/peps/pep-3151/#select
if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('read failed: {}'.format(e))
if timeout.expired():
break
return bytes(read)
def write(self, data):
"""\
Output the given byte string over the serial port. Can block if the
connection is blocked. May raise SerialException if the connection is
closed.
"""
if not self.is_open:
raise PortNotOpenError()
d = to_bytes(data)
tx_len = length = len(d)
timeout = Timeout(self._write_timeout)
while tx_len > 0:
try:
n = self._socket.send(d)
if timeout.is_non_blocking:
# Zero timeout indicates non-blocking - simply return the
# number of bytes of data actually written
return n
elif not timeout.is_infinite:
# when timeout is set, use select to wait for being ready
# with the time left as timeout
if timeout.expired():
raise SerialTimeoutException('Write timeout')
_, ready, _ = select.select([], [self._socket], [], timeout.time_left())
if not ready:
raise SerialTimeoutException('Write timeout')
else:
assert timeout.time_left() is None
# wait for write operation
_, ready, _ = select.select([], [self._socket], [], None)
if not ready:
raise SerialException('write failed (select)')
d = d[n:]
tx_len -= n
except SerialException:
raise
except OSError as e:
# this is for Python 3.x where select.error is a subclass of
# OSError ignore BlockingIOErrors and EINTR. other errors are shown
# https://www.python.org/dev/peps/pep-0475.
if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('write failed: {}'.format(e))
except select.error as e:
# this is for Python 2.x
# ignore BlockingIOErrors and EINTR. all errors are shown
# see also http://www.python.org/dev/peps/pep-3151/#select
if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('write failed: {}'.format(e))
if not timeout.is_non_blocking and timeout.expired():
raise SerialTimeoutException('Write timeout')
return length - len(d)
def reset_input_buffer(self):
"""Clear input buffer, discarding all that is in the buffer."""
if not self.is_open:
raise PortNotOpenError()
# just use recv to remove input, while there is some
ready = True
while ready:
ready, _, _ = select.select([self._socket], [], [], 0)
try:
if ready:
ready = self._socket.recv(4096)
except OSError as e:
# this is for Python 3.x where select.error is a subclass of
# OSError ignore BlockingIOErrors and EINTR. other errors are shown
# https://www.python.org/dev/peps/pep-0475.
if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('read failed: {}'.format(e))
except (select.error, socket.error) as e:
# this is for Python 2.x
# ignore BlockingIOErrors and EINTR. all errors are shown
# see also http://www.python.org/dev/peps/pep-3151/#select
if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
raise SerialException('read failed: {}'.format(e))
def reset_output_buffer(self):
"""\
Clear output buffer, aborting the current output and
discarding all that is in the buffer.
"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('ignored reset_output_buffer')
def send_break(self, duration=0.25):
"""\
Send break condition. Timed, returns to idle state after given
duration.
"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('ignored send_break({!r})'.format(duration))
def _update_break_state(self):
"""Set break: Controls TXD. When active, to transmitting is
possible."""
if self.logger:
self.logger.info('ignored _update_break_state({!r})'.format(self._break_state))
def _update_rts_state(self):
"""Set terminal status line: Request To Send"""
if self.logger:
self.logger.info('ignored _update_rts_state({!r})'.format(self._rts_state))
def _update_dtr_state(self):
"""Set terminal status line: Data Terminal Ready"""
if self.logger:
self.logger.info('ignored _update_dtr_state({!r})'.format(self._dtr_state))
@property
def cts(self):
"""Read terminal status line: Clear To Send"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for cts')
return True
@property
def dsr(self):
"""Read terminal status line: Data Set Ready"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for dsr')
return True
@property
def ri(self):
"""Read terminal status line: Ring Indicator"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for ri')
return False
@property
def cd(self):
"""Read terminal status line: Carrier Detect"""
if not self.is_open:
raise PortNotOpenError()
if self.logger:
self.logger.info('returning dummy for cd)')
return True
# - - - platform specific - - -
# works on Linux and probably all the other POSIX systems
def fileno(self):
"""Get the file handle of the underlying socket for use with select"""
return self._socket.fileno()
#
# simple client test
if __name__ == '__main__':
import sys
s = Serial('socket://localhost:7000')
sys.stdout.write('{}\n'.format(s))
sys.stdout.write("write...\n")
s.write(b"hello\n")
s.flush()
sys.stdout.write("read: {}\n".format(s.read(5)))
s.close()
-337
View File
@@ -1,337 +0,0 @@
#! python
#
# This module implements a special URL handler that wraps an other port,
# print the traffic for debugging purposes. With this, it is possible
# to debug the serial port traffic on every application that uses
# serial_for_url.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
#
# URL format: spy://port[?option[=value][&option[=value]]]
# options:
# - dev=X a file or device to write to
# - color use escape code to colorize output
# - raw forward raw bytes instead of hexdump
#
# example:
# redirect output to an other terminal window on Posix (Linux):
# python -m serial.tools.miniterm spy:///dev/ttyUSB0?dev=/dev/pts/14\&color
from __future__ import absolute_import
import logging
import sys
import time
import serial
from serial.serialutil import to_bytes
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
def sixteen(data):
"""\
yield tuples of hex and ASCII display in multiples of 16. Includes a
space after 8 bytes and (None, None) after 16 bytes and at the end.
"""
n = 0
for b in serial.iterbytes(data):
yield ('{:02X} '.format(ord(b)), b.decode('ascii') if b' ' <= b < b'\x7f' else '.')
n += 1
if n == 8:
yield (' ', '')
elif n >= 16:
yield (None, None)
n = 0
if n > 0:
while n < 16:
n += 1
if n == 8:
yield (' ', '')
yield (' ', ' ')
yield (None, None)
def hexdump(data):
"""yield lines with hexdump of data"""
values = []
ascii = []
offset = 0
for h, a in sixteen(data):
if h is None:
yield (offset, ' '.join([''.join(values), ''.join(ascii)]))
del values[:]
del ascii[:]
offset += 0x10
else:
values.append(h)
ascii.append(a)
class FormatRaw(object):
"""Forward only RX and TX data to output."""
def __init__(self, output, color):
self.output = output
self.color = color
self.rx_color = '\x1b[32m'
self.tx_color = '\x1b[31m'
def rx(self, data):
"""show received data"""
if self.color:
self.output.write(self.rx_color)
self.output.write(data)
self.output.flush()
def tx(self, data):
"""show transmitted data"""
if self.color:
self.output.write(self.tx_color)
self.output.write(data)
self.output.flush()
def control(self, name, value):
"""(do not) show control calls"""
pass
class FormatHexdump(object):
"""\
Create a hex dump of RX ad TX data, show when control lines are read or
written.
output example::
000000.000 Q-RX flushInput
000002.469 RTS inactive
000002.773 RTS active
000003.001 TX 48 45 4C 4C 4F HELLO
000003.102 RX 48 45 4C 4C 4F HELLO
"""
def __init__(self, output, color):
self.start_time = time.time()
self.output = output
self.color = color
self.rx_color = '\x1b[32m'
self.tx_color = '\x1b[31m'
self.control_color = '\x1b[37m'
def write_line(self, timestamp, label, value, value2=''):
self.output.write('{:010.3f} {:4} {}{}\n'.format(timestamp, label, value, value2))
self.output.flush()
def rx(self, data):
"""show received data as hex dump"""
if self.color:
self.output.write(self.rx_color)
if data:
for offset, row in hexdump(data):
self.write_line(time.time() - self.start_time, 'RX', '{:04X} '.format(offset), row)
else:
self.write_line(time.time() - self.start_time, 'RX', '<empty>')
def tx(self, data):
"""show transmitted data as hex dump"""
if self.color:
self.output.write(self.tx_color)
for offset, row in hexdump(data):
self.write_line(time.time() - self.start_time, 'TX', '{:04X} '.format(offset), row)
def control(self, name, value):
"""show control calls"""
if self.color:
self.output.write(self.control_color)
self.write_line(time.time() - self.start_time, name, value)
class FormatLog(object):
"""\
Write data to logging module.
"""
def __init__(self, output, color):
# output and color is ignored
self.log = logging.getLogger(output)
def rx(self, data):
"""show received data"""
if data:
self.log.info('RX {!r}'.format(data))
def tx(self, data):
"""show transmitted data"""
self.log.info('TX {!r}'.format(data))
def control(self, name, value):
"""show control calls"""
self.log.info('{}: {}'.format(name, value))
class FormatLogHex(FormatLog):
"""\
Write data to logging module.
"""
def rx(self, data):
"""show received data"""
if data:
for offset, row in hexdump(data):
self.log.info('RX {}{}'.format('{:04X} '.format(offset), row))
def tx(self, data):
"""show transmitted data"""
for offset, row in hexdump(data):
self.log.info('TX {}{}'.format('{:04X} '.format(offset), row))
class Serial(serial.Serial):
"""\
Inherit the native Serial port implementation and wrap all the methods and
attributes.
"""
# pylint: disable=no-member
def __init__(self, *args, **kwargs):
super(Serial, self).__init__(*args, **kwargs)
self.formatter = None
self.show_all = False
@serial.Serial.port.setter
def port(self, value):
if value is not None:
serial.Serial.port.__set__(self, self.from_url(value))
def from_url(self, url):
"""extract host and port from an URL string"""
parts = urlparse.urlsplit(url)
if parts.scheme != 'spy':
raise serial.SerialException(
'expected a string in the form '
'"spy://port[?option[=value][&option[=value]]]": '
'not starting with spy:// ({!r})'.format(parts.scheme))
# process options now, directly altering self
formatter = FormatHexdump
color = False
output = sys.stderr
try:
for option, values in urlparse.parse_qs(parts.query, True).items():
if option == 'file':
output = open(values[0], 'w')
elif option == 'color':
color = True
elif option == 'raw':
formatter = FormatRaw
elif option == 'rawlog':
formatter = FormatLog
output = values[0] if values[0] else 'serial'
elif option == 'log':
formatter = FormatLogHex
output = values[0] if values[0] else 'serial'
elif option == 'all':
self.show_all = True
else:
raise ValueError('unknown option: {!r}'.format(option))
except ValueError as e:
raise serial.SerialException(
'expected a string in the form '
'"spy://port[?option[=value][&option[=value]]]": {}'.format(e))
self.formatter = formatter(output, color)
return ''.join([parts.netloc, parts.path])
def write(self, tx):
tx = to_bytes(tx)
self.formatter.tx(tx)
return super(Serial, self).write(tx)
def read(self, size=1):
rx = super(Serial, self).read(size)
if rx or self.show_all:
self.formatter.rx(rx)
return rx
if hasattr(serial.Serial, 'cancel_read'):
def cancel_read(self):
self.formatter.control('Q-RX', 'cancel_read')
super(Serial, self).cancel_read()
if hasattr(serial.Serial, 'cancel_write'):
def cancel_write(self):
self.formatter.control('Q-TX', 'cancel_write')
super(Serial, self).cancel_write()
@property
def in_waiting(self):
n = super(Serial, self).in_waiting
if self.show_all:
self.formatter.control('Q-RX', 'in_waiting -> {}'.format(n))
return n
def flush(self):
self.formatter.control('Q-TX', 'flush')
super(Serial, self).flush()
def reset_input_buffer(self):
self.formatter.control('Q-RX', 'reset_input_buffer')
super(Serial, self).reset_input_buffer()
def reset_output_buffer(self):
self.formatter.control('Q-TX', 'reset_output_buffer')
super(Serial, self).reset_output_buffer()
def send_break(self, duration=0.25):
self.formatter.control('BRK', 'send_break {}s'.format(duration))
super(Serial, self).send_break(duration)
@serial.Serial.break_condition.setter
def break_condition(self, level):
self.formatter.control('BRK', 'active' if level else 'inactive')
serial.Serial.break_condition.__set__(self, level)
@serial.Serial.rts.setter
def rts(self, level):
self.formatter.control('RTS', 'active' if level else 'inactive')
serial.Serial.rts.__set__(self, level)
@serial.Serial.dtr.setter
def dtr(self, level):
self.formatter.control('DTR', 'active' if level else 'inactive')
serial.Serial.dtr.__set__(self, level)
@serial.Serial.cts.getter
def cts(self):
level = super(Serial, self).cts
self.formatter.control('CTS', 'active' if level else 'inactive')
return level
@serial.Serial.dsr.getter
def dsr(self):
level = super(Serial, self).dsr
self.formatter.control('DSR', 'active' if level else 'inactive')
return level
@serial.Serial.ri.getter
def ri(self):
level = super(Serial, self).ri
self.formatter.control('RI', 'active' if level else 'inactive')
return level
@serial.Serial.cd.getter
def cd(self):
level = super(Serial, self).cd
self.formatter.control('CD', 'active' if level else 'inactive')
return level
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if __name__ == '__main__':
ser = Serial(None)
ser.port = 'spy:///dev/ttyS0'
print(ser)
-366
View File
@@ -1,366 +0,0 @@
#! python
#
# Constants and types for use with Windows API, used by serialwin32.py
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
# pylint: disable=invalid-name,too-few-public-methods,protected-access,too-many-instance-attributes
from __future__ import absolute_import
from ctypes import c_ulong, c_void_p, c_int64, c_char, \
WinDLL, sizeof, Structure, Union, POINTER
from ctypes.wintypes import HANDLE
from ctypes.wintypes import BOOL
from ctypes.wintypes import LPCWSTR
from ctypes.wintypes import DWORD
from ctypes.wintypes import WORD
from ctypes.wintypes import BYTE
_stdcall_libraries = {}
_stdcall_libraries['kernel32'] = WinDLL('kernel32')
INVALID_HANDLE_VALUE = HANDLE(-1).value
# some details of the windows API differ between 32 and 64 bit systems..
def is_64bit():
"""Returns true when running on a 64 bit system"""
return sizeof(c_ulong) != sizeof(c_void_p)
# ULONG_PTR is a an ordinary number, not a pointer and contrary to the name it
# is either 32 or 64 bits, depending on the type of windows...
# so test if this a 32 bit windows...
if is_64bit():
ULONG_PTR = c_int64
else:
ULONG_PTR = c_ulong
class _SECURITY_ATTRIBUTES(Structure):
pass
LPSECURITY_ATTRIBUTES = POINTER(_SECURITY_ATTRIBUTES)
try:
CreateEventW = _stdcall_libraries['kernel32'].CreateEventW
except AttributeError:
# Fallback to non wide char version for old OS...
from ctypes.wintypes import LPCSTR
CreateEventA = _stdcall_libraries['kernel32'].CreateEventA
CreateEventA.restype = HANDLE
CreateEventA.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCSTR]
CreateEvent = CreateEventA
CreateFileA = _stdcall_libraries['kernel32'].CreateFileA
CreateFileA.restype = HANDLE
CreateFileA.argtypes = [LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE]
CreateFile = CreateFileA
else:
CreateEventW.restype = HANDLE
CreateEventW.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR]
CreateEvent = CreateEventW # alias
CreateFileW = _stdcall_libraries['kernel32'].CreateFileW
CreateFileW.restype = HANDLE
CreateFileW.argtypes = [LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE]
CreateFile = CreateFileW # alias
class _OVERLAPPED(Structure):
pass
OVERLAPPED = _OVERLAPPED
class _COMSTAT(Structure):
pass
COMSTAT = _COMSTAT
class _DCB(Structure):
pass
DCB = _DCB
class _COMMTIMEOUTS(Structure):
pass
COMMTIMEOUTS = _COMMTIMEOUTS
GetLastError = _stdcall_libraries['kernel32'].GetLastError
GetLastError.restype = DWORD
GetLastError.argtypes = []
LPOVERLAPPED = POINTER(_OVERLAPPED)
LPDWORD = POINTER(DWORD)
GetOverlappedResult = _stdcall_libraries['kernel32'].GetOverlappedResult
GetOverlappedResult.restype = BOOL
GetOverlappedResult.argtypes = [HANDLE, LPOVERLAPPED, LPDWORD, BOOL]
ResetEvent = _stdcall_libraries['kernel32'].ResetEvent
ResetEvent.restype = BOOL
ResetEvent.argtypes = [HANDLE]
LPCVOID = c_void_p
WriteFile = _stdcall_libraries['kernel32'].WriteFile
WriteFile.restype = BOOL
WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED]
LPVOID = c_void_p
ReadFile = _stdcall_libraries['kernel32'].ReadFile
ReadFile.restype = BOOL
ReadFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED]
CloseHandle = _stdcall_libraries['kernel32'].CloseHandle
CloseHandle.restype = BOOL
CloseHandle.argtypes = [HANDLE]
ClearCommBreak = _stdcall_libraries['kernel32'].ClearCommBreak
ClearCommBreak.restype = BOOL
ClearCommBreak.argtypes = [HANDLE]
LPCOMSTAT = POINTER(_COMSTAT)
ClearCommError = _stdcall_libraries['kernel32'].ClearCommError
ClearCommError.restype = BOOL
ClearCommError.argtypes = [HANDLE, LPDWORD, LPCOMSTAT]
SetupComm = _stdcall_libraries['kernel32'].SetupComm
SetupComm.restype = BOOL
SetupComm.argtypes = [HANDLE, DWORD, DWORD]
EscapeCommFunction = _stdcall_libraries['kernel32'].EscapeCommFunction
EscapeCommFunction.restype = BOOL
EscapeCommFunction.argtypes = [HANDLE, DWORD]
GetCommModemStatus = _stdcall_libraries['kernel32'].GetCommModemStatus
GetCommModemStatus.restype = BOOL
GetCommModemStatus.argtypes = [HANDLE, LPDWORD]
LPDCB = POINTER(_DCB)
GetCommState = _stdcall_libraries['kernel32'].GetCommState
GetCommState.restype = BOOL
GetCommState.argtypes = [HANDLE, LPDCB]
LPCOMMTIMEOUTS = POINTER(_COMMTIMEOUTS)
GetCommTimeouts = _stdcall_libraries['kernel32'].GetCommTimeouts
GetCommTimeouts.restype = BOOL
GetCommTimeouts.argtypes = [HANDLE, LPCOMMTIMEOUTS]
PurgeComm = _stdcall_libraries['kernel32'].PurgeComm
PurgeComm.restype = BOOL
PurgeComm.argtypes = [HANDLE, DWORD]
SetCommBreak = _stdcall_libraries['kernel32'].SetCommBreak
SetCommBreak.restype = BOOL
SetCommBreak.argtypes = [HANDLE]
SetCommMask = _stdcall_libraries['kernel32'].SetCommMask
SetCommMask.restype = BOOL
SetCommMask.argtypes = [HANDLE, DWORD]
SetCommState = _stdcall_libraries['kernel32'].SetCommState
SetCommState.restype = BOOL
SetCommState.argtypes = [HANDLE, LPDCB]
SetCommTimeouts = _stdcall_libraries['kernel32'].SetCommTimeouts
SetCommTimeouts.restype = BOOL
SetCommTimeouts.argtypes = [HANDLE, LPCOMMTIMEOUTS]
WaitForSingleObject = _stdcall_libraries['kernel32'].WaitForSingleObject
WaitForSingleObject.restype = DWORD
WaitForSingleObject.argtypes = [HANDLE, DWORD]
WaitCommEvent = _stdcall_libraries['kernel32'].WaitCommEvent
WaitCommEvent.restype = BOOL
WaitCommEvent.argtypes = [HANDLE, LPDWORD, LPOVERLAPPED]
CancelIoEx = _stdcall_libraries['kernel32'].CancelIoEx
CancelIoEx.restype = BOOL
CancelIoEx.argtypes = [HANDLE, LPOVERLAPPED]
ONESTOPBIT = 0 # Variable c_int
TWOSTOPBITS = 2 # Variable c_int
ONE5STOPBITS = 1
NOPARITY = 0 # Variable c_int
ODDPARITY = 1 # Variable c_int
EVENPARITY = 2 # Variable c_int
MARKPARITY = 3
SPACEPARITY = 4
RTS_CONTROL_HANDSHAKE = 2 # Variable c_int
RTS_CONTROL_DISABLE = 0 # Variable c_int
RTS_CONTROL_ENABLE = 1 # Variable c_int
RTS_CONTROL_TOGGLE = 3 # Variable c_int
SETRTS = 3
CLRRTS = 4
DTR_CONTROL_HANDSHAKE = 2 # Variable c_int
DTR_CONTROL_DISABLE = 0 # Variable c_int
DTR_CONTROL_ENABLE = 1 # Variable c_int
SETDTR = 5
CLRDTR = 6
MS_DSR_ON = 32 # Variable c_ulong
EV_RING = 256 # Variable c_int
EV_PERR = 512 # Variable c_int
EV_ERR = 128 # Variable c_int
SETXOFF = 1 # Variable c_int
EV_RXCHAR = 1 # Variable c_int
GENERIC_WRITE = 1073741824 # Variable c_long
PURGE_TXCLEAR = 4 # Variable c_int
FILE_FLAG_OVERLAPPED = 1073741824 # Variable c_int
EV_DSR = 16 # Variable c_int
MAXDWORD = 4294967295 # Variable c_uint
EV_RLSD = 32 # Variable c_int
ERROR_SUCCESS = 0
ERROR_NOT_ENOUGH_MEMORY = 8
ERROR_OPERATION_ABORTED = 995
ERROR_IO_INCOMPLETE = 996
ERROR_IO_PENDING = 997 # Variable c_long
ERROR_INVALID_USER_BUFFER = 1784
MS_CTS_ON = 16 # Variable c_ulong
EV_EVENT1 = 2048 # Variable c_int
EV_RX80FULL = 1024 # Variable c_int
PURGE_RXABORT = 2 # Variable c_int
FILE_ATTRIBUTE_NORMAL = 128 # Variable c_int
PURGE_TXABORT = 1 # Variable c_int
SETXON = 2 # Variable c_int
OPEN_EXISTING = 3 # Variable c_int
MS_RING_ON = 64 # Variable c_ulong
EV_TXEMPTY = 4 # Variable c_int
EV_RXFLAG = 2 # Variable c_int
MS_RLSD_ON = 128 # Variable c_ulong
GENERIC_READ = 2147483648 # Variable c_ulong
EV_EVENT2 = 4096 # Variable c_int
EV_CTS = 8 # Variable c_int
EV_BREAK = 64 # Variable c_int
PURGE_RXCLEAR = 8 # Variable c_int
INFINITE = 0xFFFFFFFF
CE_RXOVER = 0x0001
CE_OVERRUN = 0x0002
CE_RXPARITY = 0x0004
CE_FRAME = 0x0008
CE_BREAK = 0x0010
class N11_OVERLAPPED4DOLLAR_48E(Union):
pass
class N11_OVERLAPPED4DOLLAR_484DOLLAR_49E(Structure):
pass
N11_OVERLAPPED4DOLLAR_484DOLLAR_49E._fields_ = [
('Offset', DWORD),
('OffsetHigh', DWORD),
]
PVOID = c_void_p
N11_OVERLAPPED4DOLLAR_48E._anonymous_ = ['_0']
N11_OVERLAPPED4DOLLAR_48E._fields_ = [
('_0', N11_OVERLAPPED4DOLLAR_484DOLLAR_49E),
('Pointer', PVOID),
]
_OVERLAPPED._anonymous_ = ['_0']
_OVERLAPPED._fields_ = [
('Internal', ULONG_PTR),
('InternalHigh', ULONG_PTR),
('_0', N11_OVERLAPPED4DOLLAR_48E),
('hEvent', HANDLE),
]
_SECURITY_ATTRIBUTES._fields_ = [
('nLength', DWORD),
('lpSecurityDescriptor', LPVOID),
('bInheritHandle', BOOL),
]
_COMSTAT._fields_ = [
('fCtsHold', DWORD, 1),
('fDsrHold', DWORD, 1),
('fRlsdHold', DWORD, 1),
('fXoffHold', DWORD, 1),
('fXoffSent', DWORD, 1),
('fEof', DWORD, 1),
('fTxim', DWORD, 1),
('fReserved', DWORD, 25),
('cbInQue', DWORD),
('cbOutQue', DWORD),
]
_DCB._fields_ = [
('DCBlength', DWORD),
('BaudRate', DWORD),
('fBinary', DWORD, 1),
('fParity', DWORD, 1),
('fOutxCtsFlow', DWORD, 1),
('fOutxDsrFlow', DWORD, 1),
('fDtrControl', DWORD, 2),
('fDsrSensitivity', DWORD, 1),
('fTXContinueOnXoff', DWORD, 1),
('fOutX', DWORD, 1),
('fInX', DWORD, 1),
('fErrorChar', DWORD, 1),
('fNull', DWORD, 1),
('fRtsControl', DWORD, 2),
('fAbortOnError', DWORD, 1),
('fDummy2', DWORD, 17),
('wReserved', WORD),
('XonLim', WORD),
('XoffLim', WORD),
('ByteSize', BYTE),
('Parity', BYTE),
('StopBits', BYTE),
('XonChar', c_char),
('XoffChar', c_char),
('ErrorChar', c_char),
('EofChar', c_char),
('EvtChar', c_char),
('wReserved1', WORD),
]
_COMMTIMEOUTS._fields_ = [
('ReadIntervalTimeout', DWORD),
('ReadTotalTimeoutMultiplier', DWORD),
('ReadTotalTimeoutConstant', DWORD),
('WriteTotalTimeoutMultiplier', DWORD),
('WriteTotalTimeoutConstant', DWORD),
]
__all__ = ['GetLastError', 'MS_CTS_ON', 'FILE_ATTRIBUTE_NORMAL',
'DTR_CONTROL_ENABLE', '_COMSTAT', 'MS_RLSD_ON',
'GetOverlappedResult', 'SETXON', 'PURGE_TXABORT',
'PurgeComm', 'N11_OVERLAPPED4DOLLAR_48E', 'EV_RING',
'ONESTOPBIT', 'SETXOFF', 'PURGE_RXABORT', 'GetCommState',
'RTS_CONTROL_ENABLE', '_DCB', 'CreateEvent',
'_COMMTIMEOUTS', '_SECURITY_ATTRIBUTES', 'EV_DSR',
'EV_PERR', 'EV_RXFLAG', 'OPEN_EXISTING', 'DCB',
'FILE_FLAG_OVERLAPPED', 'EV_CTS', 'SetupComm',
'LPOVERLAPPED', 'EV_TXEMPTY', 'ClearCommBreak',
'LPSECURITY_ATTRIBUTES', 'SetCommBreak', 'SetCommTimeouts',
'COMMTIMEOUTS', 'ODDPARITY', 'EV_RLSD',
'GetCommModemStatus', 'EV_EVENT2', 'PURGE_TXCLEAR',
'EV_BREAK', 'EVENPARITY', 'LPCVOID', 'COMSTAT', 'ReadFile',
'PVOID', '_OVERLAPPED', 'WriteFile', 'GetCommTimeouts',
'ResetEvent', 'EV_RXCHAR', 'LPCOMSTAT', 'ClearCommError',
'ERROR_IO_PENDING', 'EscapeCommFunction', 'GENERIC_READ',
'RTS_CONTROL_HANDSHAKE', 'OVERLAPPED',
'DTR_CONTROL_HANDSHAKE', 'PURGE_RXCLEAR', 'GENERIC_WRITE',
'LPDCB', 'CreateEventW', 'SetCommMask', 'EV_EVENT1',
'SetCommState', 'LPVOID', 'CreateFileW', 'LPDWORD',
'EV_RX80FULL', 'TWOSTOPBITS', 'LPCOMMTIMEOUTS', 'MAXDWORD',
'MS_DSR_ON', 'MS_RING_ON',
'N11_OVERLAPPED4DOLLAR_484DOLLAR_49E', 'EV_ERR',
'ULONG_PTR', 'CreateFile', 'NOPARITY', 'CloseHandle']
-98
View File
@@ -1,98 +0,0 @@
import sys
import serial
import logging
import konfig
import time
from mysql.connector import connect, Error
from time import sleep
Log_Format = "%(levelname)s %(module)s:%(lineno)d %(asctime)s - %(message)s"
logging.basicConfig(stream=sys.stdout, format = Log_Format, level=logging.INFO)
logger = logging.getLogger()
comeTimeID = 0
def checksum(data):
checksum = 0;
len = int(data[1:3])
if(len < 99):
for i in range(1, len+3):
checksum = checksum ^ ord(data[i:i+1])
#print(str(i)+":"+str(data[i:i+1])+" "+hex(ord(data[i:i+1])))
return checksum & 0xFF;
def writeCmd(cmd,data):
length = str(len(data)+1).zfill(2)
msg = '#'+ length + cmd + data
cs = str(checksum(str.encode(msg))).zfill(3)
#print("Len: "+str(len(data)+1))
#print("MEssage: "+msg+" CS: "+cs)
con.write(str.encode(msg+cs+"\n"))
def getMaID(id):
comeTimeID = 0
try:
with connect(**konfig.datenbank()) as connection:
with connection.cursor() as cursor:
cursor.execute("SELECT id FROM user WHERE rfid = '"+str(id[0:8])+"';")
maid = cursor.fetchone()
connection.commit()
if maid == None:
writeCmd('M',"NeuerTag "+str(id[0:8]))
logger.warning("Employee not found rfID: %s",str(id[0:8]))
else:
cursor.execute("SELECT id FROM zeiten WHERE ma_id = "+maid[0]+" AND DATE(kommen) = DATE(NOW()) AND gehen = '1990-01-01 00:00:00';")
cometime = cursor.fetchone()
connection.commit()
if cometime == None:
writeCmd('C',"")
cursor.execute("INSERT INTO zeiten (kommen, gehen, ma_id) VALUES (NOW(), '1990-01-01 00:00:00', "+maid[0]+")")
connection.commit()
else:
writeCmd('G',"")
return str(cometime[0])
except:
writeCmd('M',"Datenbankverbindung\n\rfehlgeschlagen")
logger.warning("DB connection failed!")
#con = serial.Serial("COM7",115200,timeout=2,parity=serial.PARITY_NONE, dsrdtr=False)
#con.write('#')
#con.write('\n')
#sleep(2)
#writeCmd("T",str(int(time.time())))
#print(con.read_all())
con = None
while True:
try:
if(con == None):
con = serial.Serial("COM7",115200,timeout=2,parity=serial.PARITY_NONE, dsrdtr=False)
print("Reconnecting")
sleep(2)
writeCmd("T",str(int(time.time())))
line=con.readline()
if len(line) > 10:
logger.info("MSG: "+str(line)+" "+str(line[3:4]))
if(line[3:4] == b'I'):
logger.info("TAG!")
cometimeID = getMaID(line[4:4+8])
elif(line[3:4] == b'P'):
logger.info("PAUSE!")
pause = str(line(4,4+3))
if(cometimeID != None):
try:
with connect(**konfig.datenbank()) as connection:
with connection.cursor() as cursor:
cursor.execute("UPDATE zeiten SET gehen = NOW(), pause_mins="+pause+" WHERE id = "+cometimeID)
maid = cursor.fetchone()
connection.commit()
writeCmd('P',"")
except:
writeCmd('M',"Eintragen\n\rfehlgeschlagen")
logger.warning("Could not update entry with Go time")
except:
if(not(con == None)):
con.close()
con = None
logger.warning("Disconnecting")
time.sleep(2)