bpod_core.com¶
Module providing extended serial communication functionality.
Classes¶
- class bpod_core.com.ChunkedSerialReader ¶
Bases:
ProtocolA protocol for reading chunked data from a serial port.
This class provides methods to buffer incoming data and retrieve it in chunks.
- Parameters:
chunk_size (
int) – The fixed size of chunks to emit to the callback function when enough data has accumulated in the buffer.callback (
Callable[[bytearray],None]) – A function to call with each chunk of data.buffer (
bytearray|None, default:None) – Pre-allocated buffer to use for accumulation. If None, a new bytearray is created.
- connection_lost(exc) ¶
Called when the serial port is closed or the reader loop terminated otherwise.
- Parameters:
exc (
BaseException|None) – The exception that caused the connection to be closed, if any.
- connection_made(transport) ¶
Called when a connection is made.
- Parameters:
transport (
ReaderThread[Self]) – The reader thread that created this protocol instance.
- class bpod_core.com.ExtendedSerial ¶
Bases:
SerialEnhances pySerial’s
Serialwith additional functionality.- query(query, size=1) ¶
Query data from the serial port.
This method is a combination of
write()andread().- Parameters:
- Returns:
Data returned by the serial device in response to the query.
- Return type:
Examples
Send a command and read back multiple bytes:
response = serial_port.query(b'\x4A', size=4)
- query_struct(query, fmt) ¶
Query structured data from the serial port.
This method queries a specified number of bytes from the serial port and unpacks it into a tuple according to the provided format.
- Parameters:
query (
Buffer) – Query to be sent to the serial port.fmt (
str|Struct) – A pre-compiled struct or a format string compatible with thestructmodule’s format specifications.
- Returns:
A tuple containing the unpacked data read from the serial port. The structure of the tuple corresponds to the format specified in fmt.
- Return type:
Examples
Send a command and unpack the response as two unsigned 8-bit integers:
major, minor = serial_port.query_struct(b'\x4a', 'BB')Pass a pre-compiled
struct.Structto avoid re-parsing the format string on every call:fmt = struct.Struct('<2H') while acquiring: value, flag = serial_port.query_struct(b'\x4a', fmt)
- read_bool() ¶
Read a boolean value from the serial port.
- Returns:
Trueif the byte read is non-zero,Falseotherwise.- Return type:
- read_int16() ¶
Read a 16-bit signed integer from the serial port (little-endian).
- Returns:
The 16-bit signed integer read from the port.
- Return type:
- read_int32() ¶
Read a 32-bit signed integer from the serial port (little-endian).
- Returns:
The 32-bit signed integer read from the port.
- Return type:
- read_int64() ¶
Read a 64-bit signed integer from the serial port (little-endian).
- Returns:
The 64-bit signed integer read from the port.
- Return type:
- read_int8() ¶
Read an 8-bit signed integer from the serial port.
- Returns:
The 8-bit signed integer read from the port.
- Return type:
- read_struct(fmt) ¶
Read structured data from the serial port.
This method reads a specified number of bytes from the serial port and unpacks it into a tuple according to the provided format.
- Parameters:
fmt (
str|Struct) – A pre-compiled struct or a format string compatible with thestructmodule’s format specifications.- Returns:
A tuple containing the unpacked data read from the serial port. The structure of the tuple corresponds to the format specified in fmt.
- Return type:
- Raises:
struct.error – If fmt is invalid or the data cannot be unpacked.
Examples
Read one unsigned 16-bit integer followed by two unsigned 8-bit integers:
major, minor, patch = serial_port.read_struct('<HBB')Pass a pre-compiled
struct.Structto avoid re-parsing the format string on every call:fmt = struct.Struct('<HBB') while acquiring: major, minor, patch = serial_port.read_struct(fmt)
See also
Format specifications used by the
structmodule.
- read_struct_iter(fmt, n=1, *, flatten=False) ¶
Read structured data from the serial port as an iterator.
- Parameters:
- Yields:
tupleorAny– Each unpacked record as a tuple, or individual values ifflatten=True.- Return type:
Notes
All bytes are read in a single call before any records are yielded. Use
stream_struct()instead if records should be yielded as they arrive.Examples
Read three records as tuples:
for value, flag in serial_port.read_struct_iter('<HB', 3): print(value, flag)
Read two records as individual integers:
v1, f1, v2, f2 = serial_port.read_struct_iter('<HB', 2, flatten=True)See also
Format specifications used by the
structmodule.
- read_uint16() ¶
Read a 16-bit unsigned integer from the serial port (little-endian).
- Returns:
The 16-bit unsigned integer read from the port.
- Return type:
- read_uint32() ¶
Read a 32-bit unsigned integer from the serial port (little-endian).
- Returns:
The 32-bit unsigned integer read from the port.
- Return type:
- read_uint64() ¶
Read a 64-bit unsigned integer from the serial port (little-endian).
- Returns:
The 64-bit unsigned integer read from the port.
- Return type:
- read_uint8() ¶
Read an 8-bit unsigned integer from the serial port.
- Returns:
The 8-bit unsigned integer read from the port.
- Return type:
- stream_struct(fmt, n, *, flatten=True) ¶
Stream structured data from the serial port, yielding one record at a time.
- Parameters:
- Yields:
Anyortuple– Individual values ifflatten=True, otherwise one tuple per record.- Return type:
Notes
Each record is read and yielded as soon as its bytes arrive, using one
readinto()call per record. Useread_struct_iter()instead if all data is available upfront and a single read call is preferred.Examples
Stream 100 uint32 samples, yielding each as it arrives:
for sample in serial_port.stream_struct('<I', 100): process(sample)
Stream 10 records of 3 floats as tuples:
for x, y, z in serial_port.stream_struct('<3f', 10, flatten=False): print(x, y, z)
See also
Format specifications used by the
structmodule.
- verify(query=b'', expected_response=b'\\x01') ¶
Verify the response of the serial port.
This method sends a query to the serial port and checks if the response matches the expected response.
- Parameters:
- Returns:
Trueif the response matches the expected response,Falseotherwise.- Return type:
Examples
Send a handshake byte and check for acknowledgement:
if not serial_port.verify(b'\x48'): raise RuntimeError('Device did not acknowledge handshake')
- write_bool(value) ¶
Write a boolean value to the serial port.
- write_int16(value) ¶
Write a 16-bit signed integer to the serial port (little-endian).
- Parameters:
value (
int) – An integer in the range [-32768, 32767].- Returns:
Number of bytes written, or None if the write fails.
- Return type:
- Raises:
struct.error – If value is out of range.
- write_int32(value) ¶
Write a 32-bit signed integer to the serial port (little-endian).
- Parameters:
value (
int) – An integer in the range [-2147483648, 2147483647].- Returns:
Number of bytes written, or None if the write fails.
- Return type:
- Raises:
struct.error – If value is out of range.
- write_int64(value) ¶
Write a 64-bit signed integer to the serial port (little-endian).
- Parameters:
value (
int) – An integer in the range [-9223372036854775808, 9223372036854775807].- Returns:
Number of bytes written, or None if the write fails.
- Return type:
- Raises:
struct.error – If value is out of range.
- write_int8(value) ¶
Write an 8-bit signed integer to the serial port.
- Parameters:
value (
int) – An integer in the range [-128, 127].- Returns:
Number of bytes written, or None if the write fails.
- Return type:
- Raises:
struct.error – If value is out of range.
- write_struct(fmt, *data) ¶
Write structured data to the serial port.
This method packs the provided data into a binary format according to the specified format string and writes it to the serial port.
- Parameters:
fmt (
str|Struct) – A pre-compiled struct or a format string compatible with thestructmodule’s format specifications.*data (
Any) – Variable-length arguments representing the data to be packed and written, corresponding to the format specifiers in format_string.
- Returns:
The number of bytes written to the serial port, or None if the write operation fails.
- Return type:
- Raises:
struct.error – If data cannot be packed with the given format_string.
SerialTimeoutException – In case a write timeout is configured for the port and the time is exceeded.
Examples
Write a command byte followed by a 16-bit unsigned integer:
serial_port.write_struct('<BH', 0x4A, 1000)See also
Format specifications used by the
structmodule.
- write_uint16(value) ¶
Write a 16-bit unsigned integer to the serial port (little-endian).
- Parameters:
value (
int) – An integer in the range [0, 65535].- Returns:
Number of bytes written, or None if the write fails.
- Return type:
- Raises:
struct.error – If value is out of range.
- write_uint32(value) ¶
Write a 32-bit unsigned integer to the serial port (little-endian).
- Parameters:
value (
int) – An integer in the range [0, 4294967295].- Returns:
Number of bytes written, or None if the write fails.
- Return type:
- Raises:
struct.error – If value is out of range.
- write_uint64(value) ¶
Write a 64-bit unsigned integer to the serial port (little-endian).
- Parameters:
value (
int) – An integer in the range [0, 18446744073709551615].- Returns:
Number of bytes written, or None if the write fails.
- Return type:
- Raises:
struct.error – If value is out of range.
- write_uint8(value) ¶
Write an 8-bit unsigned integer to the serial port.
- Parameters:
value (
int) – An integer in the range [0, 255].- Returns:
Number of bytes written, or None if the write fails.
- Return type:
- Raises:
struct.error – If value is out of range.
- class bpod_core.com.SerialDevice ¶
Bases:
AbstractContextManagerBase class for implementing drivers for USB serial devices.
Handles connection lifecycle — opening, closing, and cleanup on garbage collection — and provides subclasses with an
ExtendedSerialconnection and port metadata. Derive from this class instead of usingserial.Serialdirectly to get automatic resource management and consistent error handling.- Parameters:
- Raises:
serial.SerialException – If the specified port does not exist.
Examples
Subclass
SerialDeviceand use_serialto communicate:class MyDevice(SerialDevice): _serial_device_name = 'My Device' def ping(self) -> bool: return self._serial.verify(b'\x48') with MyDevice('/dev/ttyACM0') as dev: assert dev.ping()
- _rename_serial_device(new_name) ¶
Rename the serial device.
Use this method to change the name of the serial device after instantiation. It will ensure that the finalizer is updated to reflect the new name.
- Parameters:
new_name (
str) – The new name of the serial device.
- close() ¶
Close the serial connection.
If the connection is already closed, this method does nothing.
- Raises:
serial.SerialException – If the connection cannot be closed.
- open() ¶
Open the serial connection.
If the connection is already open, this method does nothing.
- Raises:
serial.SerialException – If the connection cannot be opened.
- _port_info: serial.tools.list_ports_common.ListPortInfo¶
Information about the serial port associated with the device.
- _serial: ExtendedSerial¶
The serial connection to the device.
Functions¶
- bpod_core.com.find_ports(**filters) ¶
Find serial ports matching specified criteria.
Multiple filters use AND logic. Iterables within a single filter use OR logic.
- Parameters:
**filters (
str|int|Pattern[str] |None|Sequence[str|int|Pattern[str] |None|Sequence[FilterValue]]) –Port attributes to filter by. Values can be:
Scalar: exact match
Sequence: match any item (OR logic)
re.Pattern: regex match (use
re.compile())
- Returns:
Ports matching all criteria.
- Return type:
Examples
Find by vendor ID:
find_ports(vid=0x16C0)Find using regex pattern:
find_ports(device=re.compile(r'/dev/ttyACM\d+'))Find multiple values:
find_ports(pid=[0x0483, 0x048B])Combine filters:
find_ports(vid=0x16C0, device=re.compile(r'/dev/ttyACM\d+'))Notes
Strings use exact matching. Use
re.compile()for regex patterns.
- bpod_core.com.verify_serial_discovery(port, expected_message, timeout=1, trigger=None) ¶
Check if a device sends an expected discovery message on a serial port.
Opens the specified serial port and waits to receive bytes matching the expected discovery message. Optionally executes a function first, which can be used to trigger the device’s discovery routine, e.g., by sending a command.
- Parameters:
port (
str) – The serial port to read from (e.g., ‘/dev/ttyUSB0’ or ‘COM3’).expected_message (
bytes) – The exact byte sequence expected from the device.timeout (
float, default:1) – Maximum time (in seconds) to wait for the discovery message.trigger (
Callable[[],Any] |None, default:None) – A function to call before reading. Use this to trigger the device’s discovery routine.
- Returns:
True if the device sent the expected message within the timeout period, False otherwise (including if the port cannot be opened).
- Return type:
Attributes¶
- bpod_core.com.FilterValue = str | int | re.Pattern[str] | None | collections.abc.Sequence['FilterValue']¶
Type for filter values used in
find_ports().