Getting Started with the API
High-Level Device Functions
If you have a properly connected NV200Device
instance, you can start using the API of
the NV200 library. The high-level functions provide a user-friendly interface for common device
operations such as setting control modes, updating setpoints, or querying the current position.
They are designed to cover typical use cases and abstract away low-level communication details.
The following example shows, how to position the device in closed loop mode and then read the current position of the device using the high-level API:
import asyncio
from nv200.nv200_device import NV200Device
from nv200.shared_types import PidLoopMode
from nv200.connection_utils import connect_to_single_device
async def move_closed_loop():
"""
Moves the device to a specified position using closed-loop control.
"""
dev = await connect_to_single_device(NV200Device)
print(f"Connected to device: {dev.device_info}")
await dev.move_to_position(20)
await asyncio.sleep(0.2)
print(f"Current position: {await dev.get_current_position()}")
# instead of using move_to_position, you can also use two separate commands
# to set the PID mode and the setpoint
await dev.set_pid_mode(PidLoopMode.CLOSED_LOOP)
await dev.set_setpoint(0)
await asyncio.sleep(0.2)
print(f"Current position: {await dev.get_current_position()}")
if __name__ == "__main__":
asyncio.run(move_closed_loop())
Generic Read/Write Methods
These methods offer low-level access to the device’s command interface. They are useful for advanced users who need direct control or want to access features not covered by the high-level API. Some of the generic methods are:
write()
read()
read_response()
read_values()
read_float_value()
read_int_value()
read_string_value()
The following example shows how to use the generic read/write methods to read and write device parameters in a generic low-level way:
import asyncio
from nv200.nv200_device import NV200Device
from nv200.connection_utils import connect_to_single_device
async def read_write_tests():
"""
Test some generic low-level read/write methods
"""
dev = await connect_to_single_device(NV200Device)
print(f"Connected to device: {dev.device_info}")
await dev.write('cl,0')
response = await dev.read_response_string('cl')
print(repr(response))
response = await dev.read_response('set')
print(response)
response = await dev.read_values('recout,0,0,1')
print(response)
response = await dev.read_float_value('set')
print(response)
response = await dev.read_int_value('cl')
print(response)
response = await dev.read_string_value('desc')
print(response)
await dev.close()
if __name__ == "__main__":
asyncio.run(read_write_tests())
The expected output of the above example is:
Connected to device: Telnet @ 192.168.101.2 - NV200/D_NET
'cl,0\r\x00\n'
('set', ['0.000'])
['0', '0', '2.580']
0.0
0
PSH20
So if you do not find a specific function in the high-level API, you can use the generic read/write methods to access the device parameters directly. The generic methods are also useful for debugging purposes.
Command Parameter Caching
The PiezoDeviceBase
class provides an optional command result caching mechanism to optimize read
performance for piezoelectric device communication. This section explains the purpose of this cache,
how to control it, when to use it, and provides usage examples.
Purpose of the Cache
The piezoelectric devices such as the NV200/D are accessed via transport protocols like serial or telnet, which can introduce communication latency. When applications (e.g., GUIs) frequently request the same values (such as current position, mode, or configuration), re-reading those values over the wire can become inefficient.
The caching mechanism in PiezoDeviceBase
stores the result of frequently-read commands locally.
If caching is enabled and a requested command is cached, the device will not be queried again—instead,
the cached value will be returned immediately. This is especially useful for:
Polling the same parameter at high frequency
Avoiding redundant communication
Reducing delays in latency-sensitive applications
Note
Caching only applies to commands that are explicitly marked as cacheable in the concrete
device class (see below) - that means it only applies to commands that are defined in the
CACHEABLE_COMMANDS
set of the concrete device class.
How to Enable or Disable the Cache
Caching is controlled via the class variable CMD_CACHE_ENABLED
:
PiezoDeviceBase.CMD_CACHE_ENABLED = True # or False
When set to True
(default), caching is enabled for all cacheable commands defined in the concrete device’s
CACHEABLE_COMMANDS
set. When False
, all caching behavior is disabled globally.
You can override this at runtime:
import asyncio
from nv200.connection_utils import connect_to_single_device
from nv200.nv200_device import NV200Device
from nv200.device_base import PiezoDeviceBase
async def main_async():
device = await connect_to_single_device(NV200Device)
PiezoDeviceBase.CMD_CACHE_ENABLED = False # disable globally
pidmode = await device.get_pid_mode() # always reads from device
print(pidmode)
PiezoDeviceBase.CMD_CACHE_ENABLED = True # enable globally
pidmode = await device.get_pid_mode() # uses cache if available
print(pidmode)
# Running the async main function
if __name__ == "__main__":
asyncio.run(main_async())
When to Enable or Disable the Cache
✅ Enable the cache when:
Your application is the only process accessing the device.
Values you read do not change unexpectedly - such as read-only like maximum or minimum position
You want to improve performance or reduce traffic.
❌ Disable the cache when:
Multiple applications or users are interacting with the same device.
Device values may change outside your control (e.g., via a serial console).
You need guaranteed up-to-date values on each read.
Note
If there is any chance that another process modifies the device state in parallel, caching should be disabled to avoid serving outdated values.
Clearing the Command Cache
You can clear the local cache manually using the clear_cmd_cache()
method. This is useful if:
You suspect the cache is stale.
You’ve temporarily disabled caching and want to refresh once re-enabled.
You know the device state has changed externally and need to invalidate local values.
await device.get_max_position() # This will be cached
device.clear_cmd_cache() # Clear the cache
await device.device.get_max_position() # This forces a re-read from the device
Summary
Caching in PiezoDeviceBase
is a powerful tool to reduce communication overhead and
latency when interacting with piezo devices. CMD_CACHE_ENABLED
provides a simple global
toggle, and all commands listed in CACHEABLE_COMMANDS
in each concrete subclass will
automatically benefit from caching when enabled.
Actuator Configuration Backup
The NV200Device
class provides functionality to interact with the actuator’s nonvolatile
memory (EEPROM), which stores actuator-specific control parameters such as filter settings,
control loop gains, and modulation modes.
Because these parameters are persistent and can be overwritten during normal use of the Python library, it is essential to back up the original configuration before making changes. This enables safe experimentation with tuning or settings, while ensuring the ability to restore the default factory configuration if needed.
Why Back Up Actuator Parameters?
Actuators connected to the NV200 amplifier store important default control parameters in their EEPROM. These include:
Low-pass and notch filter settings
PID gains (kp, ki, kd)
Closed-loop control settings
Changing these values programmatically will overwrite them in the actuator’s nonvolatile memory. If the new parameters are incorrect or unstable, it may degrade device performance or behavior.
Warning
Always export and save the actuator configuration before writing or experimenting with actuator parameters.
Exporting Actuator Configuration
Use the export_actuator_config()
method to read and save the current actuator configuration to an INI file.
saved_path = await device.export_actuator_config(path="configs")
This will:
Query a predefined list of parameters from the actuator EEPROM.
Save them to a standard INI file.
Use the actuator’s description and serial number for the default filename if not specified.
Parameters saved include:
export_keys = [
"desc",
"acserno",
"sr",
"setlpon",
"setlpf",
"kp",
"kd",
"ki",
"notchf",
"notchb",
"notchon",
"poslpon",
"poslpf",
"modsrc",
"cl",
"pcf",
]
If no path or filename is provided, the file will be saved in the current directory under a name like:
actuator_conf_<desc>_<acserno>.ini
So for example, if the actuator description is “TRITOR100SG” and the serial number is “85533”, the file will be named:
actuator_conf_TRITOR100SG _85533.ini
This is the layout of the exported INI file for a TRITOR100SG actuator with serial number 85533:
[Actuator Configuration]
desc = TRITOR100SG
acserno = 85533
sr = 50.000
setlpon = 0
setlpf = 200
kp = 0.000
kd = 0.000
ki = 70.000
notchf = 100
notchb = 200
notchon = 0
poslpon = 1
poslpf = 1000.000
modsrc = 0
cl = 1
pcf = 0.000000e+00,0.000000e+00,0.000000e+00
Restoring Actuator Configuration
If needed, you can restore a previously saved configuration using the import_actuator_config()
method:
await device.import_actuator_config("configs/actuator_conf_TRITOR100SG _85533.ini")
This will restore the actuator parameters from the specified INI file, overwriting the current settings in the actuator’s EEPROM.
Best Practices
Always export actuator settings before making changes.
Use version control (e.g., Git) to track configuration history in team projects.
Consider re-importing the backup configuration after tests to reset the actuator to its original state.