Skip to main content

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

Core Capabilities
  • Communicates with the Smart/T-Piezo sensor over a serial port based on the Modbus RTU protocol
  • Provides the TactilePressureSDK facade 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 (TactileSdkError and 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

PlatformTypical Serial Port Name
WindowsCOM3, 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)

NameDescription
TactilePressureSDKFacade class that encapsulates DeviceAPI / ConfigAPI / PressureAPI / CalibrationAPI; recommended as the preferred entry point

3.2 Data Models

NameDescription
DeviceInfoDevice identity information, including model, protocol number/version, and firmware version
PressureFrameA frame of pressure data, including the 60-point value list, timestamp, and total pressure
FittingPointCalibration fitting point, including index, known pressure value (mN), and ADC sample value
CalibrationStatusCurrent calibration status: mode, pressure point index, fitting point index
CalibrationModeCalibration mode enum: SINGLE_POINT (100) / ALL_POINTS (101)

3.3 Exception Hierarchy

NameDescription
TactileSdkErrorBase class that catches all SDK exceptions
DeviceConnectionErrorSerial port cannot be opened / unexpectedly disconnected
CommunicationErrorTimeout / CRC failure / incomplete frame
ProtocolErrorFunction code exception / Modbus exception response code
ValidationErrorParameter out of range (address/frequency/pressure value, etc.)
CalibrationErrorCalibration 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

MethodDescription
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

MethodDescription
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:

MethodDescription
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

MethodDescription
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.

warning

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.

note

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).

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
tip

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

info

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

MethodFunction
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

MethodFunction
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

MethodFunction
read_all()Standard read, returns PressureFrame, raises an exception on failure
read_fast()High-frequency read, returns List[int] or None

sdk.calibration

MethodFunction
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

ParameterValue
Communication protocolModbus RTU
Baud rate4,000,000 bps
Data bits8
ParityNone
Stop bits1
Slave address range1–247
Broadcast address0 (used when changing the address, the device does not return a response)
Number of pressure points60
Pressure data formatuint16 × 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 rate100–200 Hz

10. Troubleshooting

Symptom / ExceptionTroubleshooting Direction
DeviceConnectionError / serial port cannot be openedCheck the serial port number; confirm that no other program (serial assistant, etc.) is occupying the port
CommunicationError / timeout or frame errorCheck the baud rate (default 4,000,000); check cable connections; confirm that slave_address matches the DIP switch
ProtocolError / function code exceptionUsually a firmware version mismatch; contact technical support
ValidationError / parameter out of rangeCheck the range of the passed parameters, e.g. address 1–247, frequency 50–200 Hz
Pressure values all 01. 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 target1. 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 NoneThis 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 addressset_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