Smart/T-Piezo Python SDK
tactile_sdk is the Python development package for the Smart/T-Piezo piezoresistive tactile skin. Based on the Modbus RTU protocol, it communicates with the sensor over a serial port and encapsulates interfaces for 60-point pressure data acquisition, device configuration, sensor calibration, and dynamic zeroing, with a measured maximum sampling rate of about 1480 Hz.
This document is based on the v0.1 version of Synria-Robotics/Electronic-Skin-ML.
1. Introduction
1.1 Core Capabilities
- Communicates with the Smart/T-Piezo sensor over a serial port based on the Modbus RTU protocol
- Provides the
TactilePressureSDKfacade class as the recommended entry point, encapsulating DeviceAPI / ConfigAPI / PressureAPI / CalibrationAPI - Supports real-time acquisition of 60-point tactile pressure;
read_fast()measured at up to about 1480 Hz - Supports two output modes: calibrated values (mN) and raw ADC values
- Supports dynamic zeroing: per-point baseline compensation at the software layer
- Supports sensor calibration: up to 11 piecewise linear fitting points, supporting single-point calibration and full calibration
- Supports device parameter read/write: address, sampling frequency, AD mask value, point area, etc.
- Provides a complete exception hierarchy (
TactileSdkErrorand its subclasses)
1.2 SDK Directory Structure
The core structure of the official repository v0.1 is as follows:
Electronic-Skin-ML/
├── tactile_sdk/ # SDK main package
│ ├── __init__.py # Public interface exports
│ ├── client.py # Facade layer: TactilePressureSDK
│ ├── exceptions.py # Cross-cutting layer: unified exception types
│ ├── models.py # Cross-cutting layer: business data models (dataclass)
│ ├── api/ # API layer: split by business domain
│ │ ├── base.py # BaseAPI: holds modbus + shared address
│ │ ├── device_api.py # DeviceAPI: device info read/write, address management
│ │ ├── config_api.py # ConfigAPI: sensor working parameter configuration
│ │ ├── pressure_api.py # PressureAPI: pressure data reading
│ │ └── calibration_api.py # CalibrationAPI: calibration workflow
│ ├── protocol/ # Protocol layer: Modbus RTU frame building and parsing
│ │ ├── constants.py # Function codes, register addresses, enum constants
│ │ ├── crc16.py # CRC16 algorithm
│ │ └── modbus_rtu.py # Frame build/send/receive/CRC check/parse
│ └── transport/ # Transport layer: serial port physical I/O
│ └── serial_transport.py # pyserial wrapper, byte-level I/O
├── examples/ # 9 complete example scripts
│ ├── 01_demo_quickstart.py # Connection verification and device info reading
│ ├── 02_demo_calibration.py # Manual input of calibration data
│ ├── 03_demo_configuration.py # Device parameter configuration
│ ├── 04_demo_read_pressure.py # High-speed continuous reading (200 Hz)
│ ├── 05_demo_record_pressure.py# Data recording to CSV
│ ├── 06_demo_recover_calibration.py # Restore factory calibration
│ ├── 07_demo_test_fps.py # Maximum sampling rate stress test
│ ├── 08_demo_zero_baseline.py # Manual dynamic zeroing
│ └── 09_demo_baseline_initialization.py # Reset dynamic zeroing
├── requirements.txt
└── actual.moduluscali.moduluscali.csv # Factory calibration data backup
2. Installation and Preparation
2.1 Environment Requirements
- Python 3.7 or above
- USB-to-serial adapter (FTDI chip recommended to avoid CH340 latency issues)
- A powered-on Smart/T-Piezo sensor
2.2 Installation Steps
Clone the specified version of the source code:
git clone https://github.com/Synria-Robotics/Electronic-Skin-ML.git -b v0.1
cd Electronic-Skin-ML
Install dependencies (only pyserial is required):
pip install -r requirements.txt
2.3 Serial Port Connection Check
| Platform | Typical Serial Port Name |
|---|---|
| Windows | COM3, COM6, etc. (check in Device Manager) |
| Linux | /dev/ttyUSB0, /dev/ttyACM0 |
| macOS | /dev/tty.usbserial-* |
On Linux, if you encounter permission issues, run:
sudo usermod -a -G dialout $USER
Log in again before accessing the serial port.
3. Public API Overview
tactile_sdk exports the following commonly used objects at the package entry:
3.1 Recommended Entry Point (Facade Layer)
| Name | Description |
|---|---|
TactilePressureSDK | Facade class that encapsulates DeviceAPI / ConfigAPI / PressureAPI / CalibrationAPI; recommended as the preferred entry point |
3.2 Data Models
| Name | Description |
|---|---|
DeviceInfo | Device identity information, including model, protocol number/version, and firmware version |
PressureFrame | A frame of pressure data, including the 60-point value list, timestamp, and total pressure |
FittingPoint | Calibration fitting point, including index, known pressure value (mN), and ADC sample value |
CalibrationStatus | Current calibration status: mode, pressure point index, fitting point index |
CalibrationMode | Calibration mode enum: SINGLE_POINT (100) / ALL_POINTS (101) |
3.3 Exception Hierarchy
| Name | Description |
|---|---|
TactileSdkError | Base class that catches all SDK exceptions |
DeviceConnectionError | Serial port cannot be opened / unexpectedly disconnected |
CommunicationError | Timeout / CRC failure / incomplete frame |
ProtocolError | Function code exception / Modbus exception response code |
ValidationError | Parameter out of range (address/frequency/pressure value, etc.) |
CalibrationError | Calibration operation failed (usually wraps the above exceptions) |
4. TactilePressureSDK Facade API
TactilePressureSDK is the facade class; all new code is recommended to use this entry point.
4.1 Constructor Parameters
TactilePressureSDK(
port, # "COM6" or "/dev/ttyUSB0"
*,
slave_address=1, # Modbus slave address (1–247), default 1
baudrate=4_000_000, # Baud rate, default 4,000,000
timeout=1.0, # Read timeout in seconds, default 1.0
send_wait_secs=0.005, # Delay after sending before waiting for response [s], default 0.005
)
4.2 Connection Management
sdk.connect() # Open the serial port (no-op if already open)
sdk.disconnect() # Close the serial port
sdk.is_connected # bool property, whether the serial port is open
# Recommended to use the context manager (automatic connect / disconnect)
with TactilePressureSDK("COM6") as sdk:
...
4.3 .device — DeviceAPI
| Method | Description |
|---|---|
get_info() | Read all device identity information at once, returning DeviceInfo |
get_model() | Read the device model, e.g. "ST-00-01" |
get_protocol_number() | Read the protocol number |
get_protocol_version() | Read the protocol version |
get_app_version() | Read the firmware App version |
get_address() | Read the current Modbus address from register 0x0001 |
set_address(new_addr, *, use_broadcast=True) | Change the device Modbus address; in broadcast mode, all API addresses within the SDK are updated synchronously |
4.4 .config — ConfigAPI
| Method | Description |
|---|---|
get/set_pressure_value_type(type) | Output type: 0 = raw ADC value, 1 = calibrated pressure value (mN) |
get/set_ad_mask_value(value) | AD mask threshold; values below this output 0 (range 0–65535) |
get/set_auto_upload_flag(enable) | Active upload enable (device pushes periodically, no polling required) |
get/set_auto_upload_frequency(freq) | Active upload frequency (50–200 Hz) |
get_pressure_point_count() | Total number of pressure points (read-only, typical value 60) |
get/set_sensor_point_area(area_mm2) | Single-point area (0–6553.5 mm², precision 0.1 mm²) |
get/set_auto_zero_enable(enable) | Power-on auto-zeroing enable (write-only, takes effect after restart) |
trigger_dynamic_zero() | Immediately zero the current pressure output (recommended to call when unloaded) |
reset_dynamic_zero() | Undo dynamic zeroing and restore the factory zero point |
4.5 .pressure — PressureAPI
The SDK provides two reading paths:
| Method | Description |
|---|---|
read_all() | Standard path, returns PressureFrame; raises CommunicationError on failure |
read_fast() | High-frequency fast path, returns List[int]; returns None on failure without raising an exception |
4.6 .calibration — CalibrationAPI
| Method | Description |
|---|---|
get/set_mode(mode) | Calibration mode: CalibrationMode.SINGLE_POINT (100) / ALL_POINTS (101) |
get/set_pressure_point(point) | Select the pressure point index to calibrate (used in single-point mode) |
get/set_fitting_point(point) | Select the fitting point index (1–11) |
get_fitting_point_ad() | Read the ADC value of the current fitting point |
set_fitting_point_pressure(pressure_mn) | Set the known pressure value (mN) corresponding to the current fitting point |
calibrate(*, use_sample=True, ad_value=None) | Perform calibration sampling |
get_status() | Read the current calibration status, returning CalibrationStatus |
clear() | Clear all calibration and restore factory state (irreversible) |
4.7 Quick Start Example
from tactile_sdk import TactilePressureSDK
with TactilePressureSDK("COM6", slave_address=1) as sdk:
# Read device information
info = sdk.device.get_info()
print(f"Device model: {info.device_model}") # "ST-00-01"
print(f"Firmware version: {info.app_version}") # "v1.0.1"
print(f"Pressure point count: {sdk.config.get_pressure_point_count()}") # 60
# Switch to calibrated value output (mN)
sdk.config.set_pressure_value_type(1)
# Read one frame
frame = sdk.pressure.read_all()
print(f"Per-point pressure: {frame.values}")
print(f"Total pressure: {frame.total_pressure} mN")
5. Example Scripts (Demos)
All examples are located in examples/. The serial port number is uniformly set to COM6. Before running, modify the PORT variable at the top according to your actual situation. SLAVE_ADDRESS must match the device's DIP switch setting (factory default is 1).
cd examples
python 01_demo_quickstart.py
5.1 01_demo_quickstart.py
Connects to the device, reads device information and basic configuration registers, used to verify whether serial communication is working properly. Output content: device model, protocol number/version, firmware version, pressure point count, active upload frequency, pressure value type, etc.
5.2 02_demo_calibration.py
Manually input calibration data. Interactive input in the format pressure(mN) AD value, supporting 1–11 fitting points. After confirming the input, it is written to the device in full calibration mode.
python 02_demo_calibration.py
# Calibration point 1: 0 10
# Calibration point 2: 100 559
# Calibration point 3: 500 1729
# Calibration point 4: ← Press Enter directly to finish
5.3 03_demo_configuration.py
Read and modify device configuration, including pressure value type, AD mask value, and Modbus address (with interactive confirmation).
5.4 04_demo_read_pressure.py
Continuously samples at a fixed target frequency of 200 Hz, printing the 60-point pressure values per frame and outputting one line of performance statistics per second:
>>> Statistics: actual sampling rate=198.3Hz | success=198 | failure=0 | error rate=0.00%
Press Ctrl+C to stop.
Core code:
import time
from tactile_sdk import TactilePressureSDK
TARGET_HZ = 200
interval = 1.0 / TARGET_HZ
next_t = time.perf_counter()
with TactilePressureSDK("COM6") as sdk:
sdk.config.set_pressure_value_type(1)
while True:
now = time.perf_counter()
if now < next_t:
time.sleep(next_t - now)
values = sdk.pressure.read_fast()
if values is not None:
process(values) # Your processing logic
next_t += interval
5.5 05_demo_record_pressure.py
Interactively configure the sampling rate (10 / 50 / 100 / 200 Hz or custom) and duration, and save the pressure data as a CSV file.
CSV format:
Timestamp, Relative time(s), Pressure point1, Pressure point2, ..., Pressure point60
1746000000.123, 0.000, 0, 128, 256, ...
5.6 06_demo_recover_calibration.py
Restore factory calibration. Write 119 to firmware register 0x0070, and the device automatically restores the factory-burned parameters, compatible with any batch/model. Includes a safety confirmation prompt.
This operation is irreversible. After running, it will overwrite all current custom calibration data.
5.7 07_demo_test_fps.py
Maximum sampling rate stress test. Calls read_fast() in an infinite loop at full speed without any rate limiting, printing performance statistics per second:
Actual frequency: 1480.2 Hz | Point count: 60 | Success: 1480 | Failure: 0 | Error rate: 0.00%
5.8 08_demo_zero_baseline.py
Manual dynamic zeroing. After triggering, the SDK captures the current 60 points and stores them as a software baseline. All subsequent reads automatically subtract per point (output[i] = max(0, raw[i] - baseline[i])). It is recommended to execute when there is no load on the sensor surface.
The software baseline is only valid during the current SDK connection lifecycle and is automatically cleared after disconnecting and reconnecting.
5.9 09_demo_baseline_initialization.py
Reset dynamic zeroing. Clears the software baseline, and the pressure values are restored to the hardware zero point at power-on.
6. Obtaining Sensor Runtime Parameters
6.1 Real-Time Pressure Data
from tactile_sdk import TactilePressureSDK
with TactilePressureSDK("COM6") as sdk:
sdk.config.set_pressure_value_type(1) # Switch to calibrated value mode
# Standard read (with error handling)
frame = sdk.pressure.read_all()
print(f"Point count = {frame.point_count}")
print(f"Per-point pressure = {frame.values}") # List[int], length 60
print(f"Total pressure = {frame.total_pressure} mN")
print(f"Timestamp = {frame.timestamp:.3f}")
# High-frequency read (for hot loops, returns None on failure)
values = sdk.pressure.read_fast()
if values is not None:
print(values)
6.2 Reading Device Register Parameters
from tactile_sdk import TactilePressureSDK
with TactilePressureSDK("COM6") as sdk:
info = sdk.device.get_info()
print(f"Device model: {info.device_model}")
print(f"Protocol number: {info.protocol_number}")
print(f"Protocol version: {info.protocol_version}")
print(f"Firmware version: {info.app_version}")
print(f"Modbus address: {sdk.device.get_address()}")
print(f"Total pressure points: {sdk.config.get_pressure_point_count()}")
print(f"Output type: {sdk.config.get_pressure_value_type()}") # 0=AD, 1=mN
print(f"AD mask value: {sdk.config.get_ad_mask_value()}")
print(f"Point area: {sdk.config.get_sensor_point_area()} mm²")
7. Calibration Operations
Each pressure point supports up to 11 piecewise linear fitting points (index 1–11), establishing a mapping from raw ADC values → actual pressure (mN).
7.1 Full Calibration (Recommended)
Apply the same set of fitting curves uniformly to all 60 pressure points:
from tactile_sdk import TactilePressureSDK, CalibrationMode
with TactilePressureSDK("COM6") as sdk:
sdk.calibration.set_mode(CalibrationMode.ALL_POINTS)
fitting_plan = [
(1, 0), # Fitting point 1: no load 0 mN
(2, 200), # Fitting point 2: apply 200 mN
(3, 500),
(4, 1000),
]
for fitting_point, pressure_mn in fitting_plan:
sdk.calibration.set_fitting_point(fitting_point)
sdk.calibration.set_fitting_point_pressure(pressure_mn)
input(f"Please apply {pressure_mn} mN and press Enter to sample...")
sdk.calibration.calibrate(use_sample=True)
ad = sdk.calibration.get_fitting_point_ad()
print(f"Fitting point {fitting_point}: {pressure_mn} mN → AD={ad}")
7.2 Single-Point Calibration
Calibrate only the specified pressure point:
with TactilePressureSDK("COM6") as sdk:
sdk.calibration.set_mode(CalibrationMode.SINGLE_POINT)
sdk.calibration.set_pressure_point(5) # Select pressure point 5
sdk.calibration.set_fitting_point(1)
sdk.calibration.set_fitting_point_pressure(0)
sdk.calibration.calibrate()
7.3 Offline Calibration (Manually Specify AD Value)
sdk.calibration.set_fitting_point(2)
sdk.calibration.set_fitting_point_pressure(1000)
sdk.calibration.calibrate(ad_value=2284) # Directly write the historical AD value
To restore factory calibration, run 06_demo_recover_calibration.py. The firmware automatically restores the factory parameters matching this device, without needing to know the specific parameter values.
8. Low-Level API Quick Reference
The following is a summary of the methods of each API subclass, for reference by advanced users who need fine-grained control. Ordinary users are recommended to use the facade layer directly (see Section 4).
sdk.device
| Method | Function |
|---|---|
get_info() | Read complete device identity information (4 0x41 requests) |
get_address() | Read the current Modbus address |
set_address(addr, use_broadcast) | Change the Modbus address (broadcast by default, SDK internal address synchronized) |
sdk.config
| Method | Function |
|---|---|
get/set_pressure_value_type(type) | Raw ADC value (0) / calibrated value mN (1) |
get/set_ad_mask_value(value) | AD mask threshold |
get/set_auto_upload_flag(enable) | Active upload enable |
get/set_auto_upload_frequency(freq) | Active upload frequency (50–200 Hz) |
get_pressure_point_count() | Total number of pressure points (read-only) |
get/set_sensor_point_area(area_mm2) | Single-point area |
trigger_dynamic_zero() | Trigger dynamic zeroing |
reset_dynamic_zero() | Reset dynamic zeroing |
sdk.pressure
| Method | Function |
|---|---|
read_all() | Standard read, returns PressureFrame, raises an exception on failure |
read_fast() | High-frequency read, returns List[int] or None |
sdk.calibration
| Method | Function |
|---|---|
get/set_mode(mode) | Calibration mode |
get/set_pressure_point(point) | Target pressure point index |
get/set_fitting_point(point) | Target fitting point index (1–11) |
get_fitting_point_ad() | Read the ADC value of the current fitting point |
set_fitting_point_pressure(mn) | Set the known pressure value of the current fitting point |
calibrate(use_sample, ad_value) | Perform calibration |
get_status() | Read the current calibration status |
clear() | Clear all calibration (irreversible) |
9. Hardware Communication Parameters
| Parameter | Value |
|---|---|
| Communication protocol | Modbus RTU |
| Baud rate | 4,000,000 bps |
| Data bits | 8 |
| Parity | None |
| Stop bits | 1 |
| Slave address range | 1–247 |
| Broadcast address | 0 (used when changing the address, the device does not return a response) |
| Number of pressure points | 60 |
| Pressure data format | uint16 × 60, little-endian (120-byte data field per frame) |
| Measured maximum sampling rate | ~1480 Hz (read_fast at full speed with no limit) |
| Recommended sampling rate | 100–200 Hz |
10. Troubleshooting
| Symptom / Exception | Troubleshooting Direction |
|---|---|
DeviceConnectionError / serial port cannot be opened | Check the serial port number; confirm that no other program (serial assistant, etc.) is occupying the port |
CommunicationError / timeout or frame error | Check the baud rate (default 4,000,000); check cable connections; confirm that slave_address matches the DIP switch |
ProtocolError / function code exception | Usually a firmware version mismatch; contact technical support |
ValidationError / parameter out of range | Check the range of the passed parameters, e.g. address 1–247, frequency 50–200 Hz |
| Pressure values all 0 | 1. Confirm set_pressure_value_type(1) has been switched to calibrated value mode; 2. Check whether get_ad_mask_value() is too high; 3. Run 06_demo_recover_calibration.py to restore factory calibration |
| Actual sampling rate falls short of target | 1. Switch to read_fast(); 2. Reduce I/O such as printing/writing files inside the loop; 3. Confirm the baud rate is 4,000,000; 4. Use an adapter with an FTDI chip; 5. Appropriately reduce send_wait_secs |
get_auto_zero_enable() returns None | This register (0x0011) is write-only in some firmware; the SDK catches the exception and returns None, which is normal behavior |
| Cannot connect after changing the address | set_address() synchronously updates the SDK internal address, so there is no need to rebuild the instance; if already disconnected, simply reconnect with the new address |