rpc --- rpc 库¶
OpenMV Cam 上的 rpc 模块允许你将 OpenMV Cam 连接到另一个微控制器或计算机,并在 OpenMV Cam 上执行远程 python(或过程)调用。如果你希望让 OpenMV Cam 能够在另一个微控制器或计算机上执行远程过程(或 python)调用,rpc 模块也支持反向操作。
如何使用本库¶
一个通过 UART 暴露单个回调的最小 从机
import rpc
import csi
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
interface = rpc.rpc_uart_slave(baudrate=115200)
def snapshot(_):
return csi0.snapshot().compress().bytearray()
interface.register_callback(snapshot)
interface.loop() # Does not return.
向从机请求一帧 JPEG 图像的对应 主机
import rpc
interface = rpc.rpc_uart_master(baudrate=115200)
result = interface.call("snapshot")
if result is None:
print("communication failed")
elif len(result) == 0:
print("remote function not registered on the slave")
else:
# result is a memoryview of the JPEG bytes returned by the slave.
print("received", len(result), "bytes")
将 rpc_uart_master / rpc_uart_slave 替换为对应的 can、i2c 或 spi 配对即可使用其他传输方式。
通常,控制器设备要使用 rpc 库时,你需要用 rpc 库创建一个接口对象。例如:
interface = rpc.rpc_uart_master(baudrate=115200)
这将创建一个用于与 rpc 从机通信的 UART 接口。
接口创建完成后,你只需执行:
memory_view_object_result = interface.call("remote_function_or_method_name", bytes_object_argument)
随后 rpc 库会尝试在从机上执行该 "remote_function_or_method_name"。远程函数或方法将接收 bytes_object_argument,其大小最多可达 2^32-1 字节。远程方法执行完成后会返回 memory_view_object_result,其大小同样最多可达 2^32-1 字节。由于参数和响应都是通用的字节容器,你可以通过 rpc 库传递任意内容并接收任意类型的响应。传递参数的一种简单方法是使用 struct.pack() 创建参数,并在另一端使用 struct.unpack() 接收参数。对于响应,另一端可以发送字符串对象或 json 字符串作为结果,主机随后可对其进行解析。
关于错误处理,如果你尝试执行一个不存在的函数或方法名,rpc_master.call() 方法将返回一个空的 bytes() 对象。如果 rpc 库未能与从机通信,rpc 库将返回 None。
为保持简单,rpc 库不会在主机与从机设备之间维持连接。rpc_master.call() 方法封装了尝试连接从机、启动远程函数或方法的执行以及获取结果的全过程。
现在,在从机这一侧,你需要创建一个 rpc 接口来与主机通信。其形式如下:
interface = rpc.rpc_uart_slave(baudrate=115200)
这将创建用于与 rpc 主机通信的 UART 接口层。
创建从机接口后,你需要通过该接口对象注册主机可以调用的回调:
def remote_function_or_method_name(memoryview_object_argument):
<lots of code>
return bytes_object_result
interface.register_callback(remote_function_or_method_name)
你可以在从机上注册任意数量的回调。最后,注册完所有回调后,你只需执行:
interface.loop()
在从机上启动 rpc 库并开始监听主机。请注意,rpc_slave.loop() 方法不会返回。
class rpc -- rpc 基类¶
rpc 基类由 rpc_master 和 rpc_slave 类重新实现以创建主机和从机接口。它不应被直接使用。
- class rpc.rpc¶
创建一个
rpc对象。不应被直接使用。- get_bytes(buff: bytearray | memoryview, timeout_ms: int) bytes | None¶
由特定传输方式的子类重新实现。在
timeout_ms毫秒内用来自底层接口的字节填充buff。超时时返回None。
- put_bytes(data: bytes | memoryview, timeout_ms: int) None¶
由特定传输方式的子类重新实现。在
timeout_ms毫秒内通过底层接口发送data。
- stream_reader(call_back: Callable[[memoryview], None], queue_depth: int = 1, read_timeout_ms: int = 5000) None¶
从远程
rpc.stream_writer接收一连串负载数据。应在双方完成同步后,从rpc_slave回调内部(或在rpc_master.call成功后直接)调用。call_back—— 每收到一个负载就以call_back(data)形式调用一次的可调用对象,其中data为memoryview。返回值会被忽略。queue_depth—— 写入端在等待读取端之前允许发送的传输中帧数。较高的值会以内存为代价提高吞吐量。read_timeout_ms—— 每个负载的等待毫秒数。
发生任何错误时返回。要取消,可在
call_back内部抛出异常;远程端将会超时。
- stream_writer(call_back: Callable[[], bytes | memoryview], write_timeout_ms: int = 5000) None¶
向远程
rpc.stream_reader发送一连串负载数据。应在双方完成同步后,从rpc_slave回调内部(或在rpc_master.call成功后直接)调用。call_back—— 不带参数调用的可调用对象,返回下一个要发送的bytes或memoryview负载。write_timeout_ms—— 发送每个负载时的等待毫秒数。
发生任何错误时返回。要取消,可在
call_back内部抛出异常;远程端将会超时。
class rpc_master -- rpc_master 基类¶
rpc_master 是一个基类。请使用特定传输方式的子类之一(rpc_can_master、rpc_i2c_master、rpc_spi_master、rpc_uart_master)。
- class rpc.rpc_master¶
创建一个
rpc_master对象。不应被直接使用。- call(name: str, data: bytes = bytes(), send_timeout: int = 1000, recv_timeout: int = 1000) memoryview | None¶
在从机设备上执行一次远程调用。
name—— 要执行的远程函数或方法的字符串名称。data—— 作为参数传递给远程函数的bytes类对象。send_timeout—— 在连接从机并启动远程函数执行期间的等待毫秒数。一旦主机开始发送参数,此值便不再适用;该库允许参数传输最多耗时 5 秒。recv_timeout—— 等待从机开始返回响应的毫秒数。一旦主机开始接收响应,此值便不再适用;该库允许响应传输最多耗时 5 秒。
成功时返回响应的
memoryview;如果远程名称在从机上不存在,则返回空的bytes();通信失败时返回None。
class rpc_slave -- rpc_slave 基类¶
rpc_slave 是一个基类。请使用特定传输方式的子类之一(rpc_can_slave、rpc_i2c_slave、rpc_spi_slave、rpc_uart_slave)。
- class rpc.rpc_slave¶
创建一个
rpc_slave对象。不应被直接使用。- register_callback(cb: Callable[[memoryview], bytes | memoryview]) None¶
注册一个主机可以按名称调用的回调。
cb是一个接受一个memoryview参数并返回bytes类对象的可调用对象。该回调的__name__被用作查找键。
- schedule_callback(cb: Callable[[], None]) None¶
将
cb(一个不带参数的可调用对象)安排为执行一次,时机为当前正在运行的 rpc 回调成功将其响应返回给主机之后立即执行。必须从 rpc 回调内部调用。这允许长时间运行的工作或rpc.get_bytes/rpc.put_bytes直通传输在两次 rpc 事务之间运行。如需重复执行,请在每次调用时重新注册。
- setup_loop_callback(cb: Callable[[], None]) None¶
注册
cb(一个不带参数的可调用对象),使其在rpc_slave.loop的每次迭代时被调用。与rpc_slave.schedule_callback不同,此回调会保持注册状态。它必须是非阻塞的;调用频率是可变的。
class rpc_can_master -- CAN 主机接口¶
通过 CAN 控制另一个 rpc 设备。
- class rpc.rpc_can_master(message_id: int = 0x7FF, bit_rate: int = 250000, sample_point: float = 75, can_bus: int = 2)¶
message_id—— 用于数据传输的 11 位 CAN 消息 id。bit_rate—— 以比特每秒为单位的 CAN 比特率。sample_point—— Tseg1/Tseg2 采样点百分比(例如 50.0、62.5、75、87.5)。can_bus—— CAN 外设编号。
主机和从机的
message_id与bit_rate必须匹配。总线必须用 120 欧姆电阻端接。
class rpc_can_slave -- CAN 从机接口¶
通过 CAN 受另一个 rpc 设备控制。
class rpc_i2c_master -- I2C 主机接口¶
通过 I2C 控制另一个 rpc 设备。
class rpc_i2c_slave -- I2C 从机接口¶
通过 I2C 受另一个 rpc 设备控制。
class rpc_spi_master -- SPI 主机接口¶
通过 SPI 控制另一个 rpc 设备。
- class rpc.rpc_spi_master(cs_pin: str = 'P3', freq: int = 1000000, clk_polarity: int = 1, clk_phase: int = 0, spi_bus: int = 2)¶
cs_pin—— 片选引脚名称。freq—— 以 Hz 为单位的 SPI 总线时钟频率。clk_polarity—— 空闲时的时钟电平(0 或 1)。clk_phase—— 在第一个(0)或第二个(1)时钟边沿采样数据。spi_bus—— SPI 外设编号。
主机和从机设置必须匹配。将 CS、SCLK、MOSI、MISO 直接连接。两个设备必须共地。
class rpc_spi_slave -- SPI 从机接口¶
通过 SPI 受另一个 rpc 设备控制。
class rpc_uart_master -- UART 主机接口¶
通过异步串口(UART)控制另一个 rpc 设备。
class rpc_uart_slave -- UART 从机接口¶
通过异步串口(UART)受另一个 rpc 设备控制。