Endpoints and Autodetection¶
LUCIDAC Endpoints are URL-like notations for addressing the connection to a LUCIDAC device. The concept is described in the Firmware docs and is similar to the NI VISA resource syntax
Available endpoints in lucipy¶
Which endpoint notation and protocol support is available depends on the client implementation. Lucipy understands the following endpoints:
- USB Serial (virtual terminal) speaking JSONL:
serial:/
A device name is expected afterwards which is immediately passed to the pySerial constructor. Examples are
serial://dev/ttyACM0
on GNU Linux/Mac OS X orserial:/COM0
on MS Windows. USB Serial connection is typically error-prone, in particular at startup when the buffers still can be filled with old contents. When using the serial connection, calls tohc.slurp()
can help to clear the buffers.- “Raw” TCP/IP speaking JSONL:
tcp:/
This is the native protocol of the LUCIDAC and the most realiable way to connect to the LUCIDAC. Typical examples are a Host name
tcp://my-lucidac.local.
or an IPv4 addresstcp://192.168.150.229
. An optional port can be given,tcp://192.168.150.229:5732
.- Integrated Simulator/Emulator:
emu:/
This will emulate a socket to the lucipy-integrated LUCIDAC Emulator (see LUCIDAC emulation). Note that this is also possible by starting the Emulator at another place and connecting via something like
tcp://localhost:1234
. However, this way it acts as a shorthand for starting up the emulator at the same time as the client, one does not have to transfer the TCP port information. Furthermore, the connection does not use TCP/IP but is just a python-internal function call (this is what was refered to an emulated socket in the beginning).To use this endpoint, just instanciate with
LUCIDAC("emu:/")
. There are no further paths in this URL. However, the optional argument?debug
can be attached to start the python debugger if the Emulator crashes.- Device autodetection:
zeroconf:/
Use this endpoint string to explicitely use autodetection even in the presence of an environment variable
LUCIDAC_ENDPOINT
. In the same way asemu:/
, this endpoint URL supports no further arguments.
Other endpoints not listed here are explicitely not supported, in particular websocket and HTTP endpoints. If you need to make use of them, consider using a proxy such as lucigo. In particular, a typical need is to proxy an USB virtual serial terminal over TCP/IP and there are many solutions for this listed at the previous link.
LUCIDAC autodetection¶
For convenience, the client code allows for autodetection of the endpoint using MDNS/Zeroconf. This works by making an instance without providing the endpoint:
>>> from lucipy import LUCIDAC
>>> hc = LUCIDAC() # this will trigger the autodetection
Typically, the autodetection connects to the first LUCIDAC found in the network and warns if multiple have been found.
Direct access to the underlying API should not be neccessary, but is possible
with import lucipy.detect
. The folling reference shows the exposed
functions.
Note
As a design philosophy in lucipy, there are no dependencies. Therefore, autodetection will only work if you have the zeroconf and/or pySerial libraries installed.
If these dependencies are not installed, the code will print warnings suggesting you to install them in order to make autodetection work.
Environment variable¶
Conveniently, you can use the operating system environment variable
LUCIDAC_ENDPOINT
. This can help to keep your scripts clean of volatile,
frequently changing and probably meaningless IP addresses and ports.
Usage is, for instance, in your operating systems shell
export LUCIDAC_ENDPOINT="tcp://10.10.77.123"``
python some-script-using-luci.py
Note that if you have set the environment variable LUCIDAC_ENDPOINT
, this will
effectively overwrite (and thus disable) the autodetection, except you call
LUCIDAC("zeroconf:/")
explicitely in your code.
Code refererence¶
This is a small interactive command line script for discovering/detecting LUCIDACs. It will look for the Teensy Microcontroller (Hybrid Controller) both directly connected over USB Serial Device as well as Network services announced by MDNS/Zeroconf over the network (local IPv4 broadcast domain). The network lookup happens typically within a few hundred milliseconds. If you want to wait for more then one device, use the –all option.
-
class
lucipy.detect.
Endpoint
(endpoint)[source]¶ This class models the LUCIDAC/REDAC endpoint URI convention in lucipy. Given there is no proper URI object in python, we make use of
urllib.urlparse
. This class is used for instance inlucipy.synchc.endpoint2socket()
.Create instances either directly or via helpers which prepend the correct scheme:
>>> Endpoint("tcp://123.123.123.123:7890") Endpoint("tcp://123.123.123.123:7890") >>> Endpoint.fromJSONL("localhost", 1234) Endpoint("tcp://localhost:1234")
For serial devices, there is a little extra work done ontop of URL parsing, which makes the syntax less weird in contexts where URIs are typically not used. Serial devices just don’t follow the double slash convention but more a kio/gfs single slash convention:
>>> Endpoint("serial:/dev/ttyACM0") # device file at Linux/Mac Endpoint("serial:/dev/ttyACM0") >>> Endpoint.fromDevice("/dev/ttyACM0") Endpoint("serial:/dev/ttyACM0") >>> Endpoint("serial:/COM0") Endpoint("serial:/COM0") >>> Endpoint.fromDevice("COM0") # port name at Windows Endpoint("serial:/COM0")
Endpoints with scheme only are fine and used in the code. Note that schemes are the only part of the URL which is always canonically lowercased:
>>> Endpoint("EMU:") Endpoint("emu:")
An example of an endpoint which uses all fields:
>>> e = Endpoint("TCP://myuser:mypass@FOO.bar:4711?flitz=bums&baz=bla") >>> e.user 'myuser' >>> e.args {'flitz': 'bums', 'baz': 'bla'} >>> e Endpoint("tcp://myuser:foo.bar:4711?flitz=bums&baz=bla")
-
scheme
¶ Scheme, such as “serial”, “tcp”, etc.
-
user
¶ Username for login, if present. None is a valid value.
-
password
¶ Password for login, if present. None is a valid value.
-
port
¶ TCP/IP Port as integer. If not given, defaults to default TCP port.
-
args
¶ Further query arguments from the URL
-
host
¶ Host or device name. Note that hostnames are transfered to lowercase while pathnames will not.
-
-
lucipy.detect.
can_resolve
(hostname, target_ip: str = None) → Optional[str][source]¶ gethostbyname but without the exceptions, i.e. with a boolean representation of the return value
-
lucipy.detect.
can_resolve_to
(hostname, expected_ip: str)[source]¶ Checks whether the host system can resolve a given (zeroconf) DNS name
-
lucipy.detect.
detect_usb_teensys
() → List[lucipy.detect.Endpoint][source]¶ Yields all found endpoints on local system using serial.tools.list_ports, requires pyserial
-
lucipy.detect.
detect_network_teensys
(zeroconf_timeout=500) → List[lucipy.detect.Endpoint][source]¶ Yields all endpoints in the local broadcast domain using Zeroconf, requires python zeroconf package
-
lucipy.detect.
detect
(single=False, prefer_network=True, zeroconf_timeout=500)[source]¶ Yields or returns possible endpoints using all methods. This function will raise an ModuleNotFoundError if a library is not available which might have found more.
- Parameters
single – Return only first found instance or None, if nothing found. If this option is False, this function will return an array of endpoints discovered using all methods.
zeroconf_timeout – Maximum search time: How long to wait for zeroconf answers, in milliseconds. Set to 0 or None for unlimited search.
prefer_network – Return network result first. Typically a TCP/IP connection is faster and more reliable then the USBSerial connection.