:mod:`rpc` --- rpc library ========================== .. module:: rpc :synopsis: rpc library OpenMV Cam上的``rpc``模块允许您将OpenMV Cam连接到另一个微控制器或计算机,并对OpenMV Cam执行远程python(或程序)调用。 如果您希望您的OpenMV Cam能够在另一个微控制器或计算机上执行远程程序(或python)调用,那么 ``rpc`` 模块也允许反向操作。 如何使用库 ---------------------- 请在OpenMV IDE的 ``Remote Control`` 下打开示例代码。 您需要编辑示例代码以选择要使用的界面并使用脚本使用的设置。 通常,为了让控制器设备使用 ``rpc`` 库,您将使用 ``rpc`` 库创建一个接口对象。例如:: interface = rpc.rpc_uart_master(baudrate=115200) 这将创建一个 UART 接口来与 ``rpc`` 从设备通信。 一旦创建了接口,你只需执行:: memory_view_object_result = interface.call("remote_function_or_method_name", bytes_object_argument) 并且 ``rpc`` 库将尝试在从属设备上执行该 ``"remote_function_or_method_name"`` 。 远程函数或方法将接收大小可达 2^32-1 字节的 ``bytes_object_argument`` 。 一旦远程方法完成执行,它将返回一个 ``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) 这将创建UART接口层来与 ``rpc`` 主机对话。 创建从接口后,您需要注册主接口对象可以调用的回调:: def remote_function_or_method_name(memoryview_object_argument): return bytes_object_result interface.register_callback(remote_function_or_method_name) 您可以在slave上注册任意多的回调函数。 最后,一旦你完成了回调,你只需要执行:: interface.loop() 在slave上启动``rpc``库并开始监听master。请注意,``rpc_slave.loop()`` 方法不返回。 此外,为了使您的 slave 更健壮地抵抗错误, 您可能需要将 ``rpc_slave.loop()`` 与 ``try:`` 和 ``except:`` 包装起来,以应对回调方法可能抛出的任何异常。 ``rpc`` 库本身不会生成任何异常。 注意:传递大型数据结构(如 jpeg 图像)可能会耗尽 OpenMV Cam 上的堆并生成 `MemoryError` 异常。 就是这样! ``rpc`` 库被设计成易于使用。 class rpc - rpc virtual class ----------------------------- rpc基类由 `rpc_master` 和 `rpc_slave` 类重新实现,以创建主从接口。它是一个纯虚拟类,不能直接使用。 构造函数 ~~~~~~~~~~~~ .. class:: rpc.rpc() 创建rpc对象。此构造函数不能直接使用。 方法 ~~~~~~~~~~~~ .. method:: rpc.get_bytes(buff, timeout_ms): 这个方法需要通过特定的接口类 `rpc_master` 和 `rpc_slave` 来重新实现。 它应该填充 ``buff`` 参数,该参数是来自接口的字节的 `bytearray` 或 `memoryview` 对象, 等于 ``timeout_ms`` 毫秒内的 ``buff`` 对象的长度。超时时,此方法应返回 ``None`` 。 请注意,对于 master 和 slave 同步,此方法应尝试始终在至少 ``timeout_ms`` 毫秒内完成而不是更快, 因为 `rpc_master` 和 `rpc_slave` 对象将自动增加 ``timeout_ms`` 以进行同步。 .. method:: rpc.put_bytes(data, timeout_ms): 这个方法需要通过特定的接口类 `rpc_master` 和 `rpc_slave` 来重新实现。 如果它完成得比超时快,那没问题。预计没有返回值。 .. method:: rpc.stream_reader(call_back, queue_depth=1, read_timeout_ms=5000): 这个方法是直接调用的。在主设备和从设备同步返回回调后,可能会调用 ``stream_reader`` 以尽快从主设备或从设备接收数据。 ``call_back`` 将使用 ``stream_writer`` 发送的 ``bytes_or_memory_view argument`` 重复调用。 ``call_back`` 不会返回任何东西。 ``queue_depth`` 定义了 ``stream_writer`` 在减慢和等待 ``stream_reader`` 之前可能生成的数据帧数。 更高的 ``queue_depth`` 值会导致更高的性能(在一定程度上),但需要 ``stream_reader`` 能够在其接口层处理未完成的数据包。 如果你让 ``queue_depth`` 大于 1,那么 ``call_back`` 应该很快返回而不是阻塞。 最后, ``read_timeout_ms`` 定义了等待接收每个回调的 ``bytes_or_memory_view`` 有效载荷的毫秒数。 在任何错误 ``stream_reader`` 都会返回。主从设备可以在之后再次尝试设置流以继续。 如果需要取消 ``stream_reader`` ,只需在``call_back``中抛出一个异常并捕获它。 远程端将自动超时。 .. method:: rpc.stream_writer(call_back, write_timeout_ms=5000): 此方法旨在直接调用。在主设备和从设备同步返回 ``callback`` 后,可能会调用 ``stream_writer`` 以尽可能快地从主设备或从设备发送数据。 ``call_back`` 将被重复调用,并且应该返回一个 ``bytes_or_memory_view`` 对象,该对象将被发送到 ``stream_reader``。 ``call_back`` 不应接受任何参数。最后,``write_timeout_ms`` 定义了等待发送 ``call_back`` 返回的 ``bytes_or_memory_view`` 对象的毫秒数。 在任何错误 ``stream_writer`` 都会返回。主从设备可以在之后再次尝试设置流以继续。 如果需要取消 ``stream_reader`` ,只需在``call_back``中抛出一个异常并捕获它。 远程端将自动超时。 class rpc_master - rpc_master virtual class ------------------------------------------- rpc_master是一个纯虚拟类,不能直接使用。特定的接口类应该重新实现 rpc_master。 构造函数 ~~~~~~~~~~~~ .. class:: rpc.rpc_master() 创建rpc_master对象。此构造函数不能直接使用。 方法 ~~~~~~~~~~~~ .. method:: rpc_master.call(name, data=bytes(), send_timeout=1000, recv_timeout=1000): 在从设备上执行远程调用。 ``name`` 是要执行的远程函数或方法的字符串名称。 ``data`` 是类似 ``bytes`` 的对象,它将作为远程函数或方法的参数发送以执行。 ``send_timeout`` 定义尝试连接到从属设备并让它执行远程功能或方法时要等待的毫秒数。 一旦主设备开始向从设备发送参数,``send_timeout`` 就不再适用。库将允许发送参数最多需要 5 秒。 ``recv_timeout`` 定义从服务器开始执行远程方法以接收响应后等待的毫秒数。 请注意,一旦 master 开始接收响应, ``recv_timeout`` 将不适用。库将允许接收响应最多需要 5 秒。 注意,一个包含 ``data`` 副本的新数据包将在 ``rpc`` 库内部创建。 如果试图传递非常大的数据,您可能会在OpenMV Cam上遇到内存问题。 class rpc_slave - rpc_slave virtual class ----------------------------------------- rpc_slave是一个纯虚拟类,不能直接使用。特定的接口类应该重新实现 rpc_slave。 构造函数 ~~~~~~~~~~~~ .. class:: rpc.rpc_slave() 创建rpc_slave对象。此构造函数不能直接使用。 方法 ~~~~~~~~~~~~ .. method:: rpc_slave.register_callback(cb): 注册一个可由主设备执行的回调。 回调应该接受一个参数,该参数将是一个 ``memoryview`` 对象,并且它应该返回一个 ``bytes()`` 之类的对象作为结果。 如果可能,回调应在 1 秒内返回。 .. method:: rpc_slave.schedule_callback(cb): 执行 ``rpc_slave.loop()`` 后,就不可能在 ``rpc`` 库之外执行长时间运行的操作。 ``schedule_callback`` 允许您在完成回调后暂时退出 ``rpc`` 库。 您应该在执行 ``rpc`` 回调方法期间执行 ``schedule_callback`` 以注册一个新的非 rpc 回调, 该回调将在您执行 ``schedule_callback`` 的回调成功完成后立即执行。 函数或方法不应接受任何参数。在注册的回调返回后,必须在下一个父回调中再次注册。 在父回调发生任何错误时,注册的回调将不会被调用,必须重新注册。以下是如何使用它:: def some_function_or_method_that_takes_a_long_time_to_execute(): def normal_rpc_call_back(data): interface.schedule_callback(some_function_or_method_that_takes_a_long_time_to_execute) return bytes(response) interface.register_callback(normal_rpc_call_back) interface.loop() ``schedule_callback`` 特别允许您使用 ``get_bytes`` 和 ``put_bytes`` 方法在一个设备和另一个设备之间进行直通数据传输, 而没有限制在 ``rpc`` 库内部移动的数据大小的打包成本,而不会耗尽 OpenMV Cam 上的内存。 .. method:: rpc_slave.setup_loop_callback(cb): 循环回调在 ``rpc_slave.loop()`` 的每次循环迭代中被调用。 与 ``rpc.schedule_callback()`` 回调不同,此回调在注册一次后保持注册状态。 您可以使用循环回调来闪烁活动 LED 或类似的东西。 你不应该使用循环回调来执行任何阻塞代码,因为这会妨碍从主节点轮询通信。 此外,循环回调将以可变速率调用,具体取决于主设备尝试执行的回调的时间和内容。 鉴于此,循环回调不适用于任何需要以固定频率执行的方法。 在 OpenMV Cam 上,如果您需要以固定频率执行某事,您应该在执行 ``rpc_slave.loop()`` 之前设置一个计时器, 并使用基于计时器中断的回调以固定频率执行某些功能或方法。请参阅如何编写中断处理程序以获取更多信息。 注意: `Mutex` 库与 ``rpc`` 库一​​起安装在您的 OpenMV Cam 上。 .. method:: rpc_slave.loop(recv_timeout=1000, send_timeout=1000): 在从设备上开始执行 ``rpc`` 库以接收数据。此方法不返回(除非通过回调异常)。 在执行此方法之前,您应该先注册所有回调。但是,可以在之前注册的正在执行的回调中注册新的回调。 ``recv_timeout`` 定义在再次尝试之前等待从主设备接收命令的时间。 ``send_timeout`` 定义了从设备在返回尝试接收之前等待主设备接收回调响应的时间。 在再次尝试接收之前将执行循环回调。 class rpc_can_master - CAN Master Interface ----------------------------------------- 通过 CAN 控制另一个 ``rpc``设备。 构造函数 ~~~~~~~~~~~~ .. class:: rpc.rpc_can_master(message_if=0x7FF, bit_rate=250000, sampling_point=75): 创建一个CAN ``rpc`` 主设备 。这个接口可以到1 Mb/s。 * message_id - 在CAN总线(11位)上用于数据传输的CAN消息。 * bit_rate - CAN 比特率. * sampling_point - Tseg1/Tseg2 比率. 一般是 75%. (50.0, 62.5, 75, 87.5, 等。) 注意:主从消息 ID 和比特率必须匹配。连接主机高那么从机也高,主机低到那么从机也低。CAN总线必须以 120ohms 终止。 在STM32上使用CAN2。 class rpc_can_slave - CAN Slave Interface ----------------------------------------- 通过 CAN 被另一个 ``rpc`` 设备控制。 构造函数 ~~~~~~~~~~~~ .. class:: rpc.rpc_can_slave(message_id=0x7FF, bit_rate=250000, sampling_point=75): 创建一个CAN ``rpc`` slave 。这个接口可以到1 Mb/s。 * message_id - 在CAN总线(11位)上用于数据传输的CAN消息。 * bit_rate - CAN 比特率。 * sampling_point - Tseg1/Tseg2 比率。 一般是 75%. (50.0, 62.5, 75, 87.5, 等。) 注意:主从消息 ID 和比特率必须匹配。连接主机高那么从机也高,主机低到那么从机也低。CAN总线必须以 120ohms 终止。 在STM32上使用CAN2。 class rpc_i2c_master - I2C Master Interface ------------------------------------------- 超过I2C控制另一个``rpc``设备。 构造函数 ~~~~~~~~~~~~ .. class:: rpc.rpc_i2c_master(slave_addr=0x12, rate=100000) 创建一个I2C ``rpc`` 主设备。这个接口可以到1 Mb/s。 * slave_addr - I2C 地址 * rate - I2C 总线时钟频率。. 注意:主地址和从地址必须匹配。将 master scl 连接到 slave scl,将 master sda 连接到 slave sda。您必须使用外部上拉。 最后,两个设备必须共地。 class rpc_i2c_slave - I2C Slave Interface ------------------------------------------- 通过I2C被另一个``rpc`` 装置控制。 构造函数 ~~~~~~~~~~~~ .. class:: rpc.rpc_i2c_slave(slave_addr=0x12) 创建一个I2C ``rpc` `slave。这个接口可以到1 Mb/s。 * slave_addr - I2C 地址 注意:主地址和从地址必须匹配。将 master scl 连接到 slave scl,将 master sda 连接到 slave sda。您必须使用外部上拉。 最后,两个设备必须共地。 在STM32上使用I2C2. class rpc_spi_master - SPI Master Interface ------------------------------------------- 通过SPI控制另一个 ``rpc`` 设备。 构造函数 ~~~~~~~~~~~~ .. class:: rpc.rpc_spi_master(cs_pin="P3", freq=10000000, clk_polarity=1, clk_phase=0) 创建SPI ``rpc`` 主服务器。这个接口可以到80mb /s。 * cs_pin - Slave 选择引脚 * freq - SPI 总线时钟频率 * clk_polarity - 空闲时钟电平(0或1) * clk_phase - 在时钟的第一(0)或第二(1)边采样数据。 注意:主从设置必须匹配。将 CS、SCLK、MOSI、MISO 连接到 CS、SCLK、MOSI、MISO。 最后,两个设备必须共地。 在STM32上使用SPI2。 class rpc_spi_slave - SPI Slave Interface ------------------------------------------- 通过SPI被另一个 ``rpc`` 设备控制。 .. class:: rpc.rpc_spi_slave(cs_pin="P3", clk_polarity=1, clk_phase=0) 创建一个SPI ``rpc`` 从设备。这个接口可以到80mb /s。 * cs_pin - Slave 选择引脚 * clk_polarity - 空闲时钟电平(0或1) * clk_phase - 在时钟的第一(0)或第二(1)边采样数据。 注意:主从设置必须匹配。将 CS、SCLK、MOSI、MISO 连接到 CS、SCLK、MOSI、MISO。 最后,两个设备必须共地 在STM32上使用SPI2。 class rpc_uart_master - UART Master Interface ------------------------------------------- 在异步串行(UART)上控制另一个 ``rpc`` 设备。 .. class:: rpc.rpc_uart_master(baudrate=115200) 创建一个UART ``rpc`` master。这个接口可以移到7.5 Mb/s。 * baudrate - 串行波特率. 注意:主从波特率必须匹配。将主 tx 连接到从 rx,将主 rx 连接到从 tx。 最后,两个设备必须共地。 在STM32上使用UATR3。 class rpc_uart_slave - UART Slave Interface ------------------------------------------- 通过异步串行(UART)被另一个 ``rpc`` 设备控制。 .. class:: rpc.rpc_uart_slave(baudrate=115200) 创建一个UART ``rpc`` 从设备。这个接口可以到7.5 Mb/s。 * baudrate - 串行波特率 注意:主从波特率必须匹配。将主 tx 连接到从 rx,将主 rx 连接到从 tx。最后,两个设备必须共享一个共同点。 在STM32上使用UATR3。 class rpc_usb_vcp_master - USB VCP Master Interface ------------------------------------------- 通过USB虚拟COM端口被另一个 ``rpc`` 设备控制。 .. class:: rpc.rpc_usb_vcp_master() 创建USB VCP ``rpc`` 主设备。这个接口可以超过12mb /s。 class rpc_usb_vcp_slave - USB VCP Slave Interface ------------------------------------------- 通过USB虚拟COM端口被另一个 ``rpc`` 设备控制。 .. class:: rpc.rpc_usb_vcp_slave() 创建USB VCP ``rpc`` 从设备。这个接口可以超过12mb /s。 class rpc_wifi_master - WiFi Master Interface ------------------------------------------- 通过WiFi被另一个 ``rpc`` 设备控制。 .. class:: rpc.rpc_wifi_master(ssid, ssid_key, ssid_security, ip, port=0x1DBA, mode=network.WINC.MODE_STA, static_ip=None) 创建WiFi ``rpc`` 主设备。这个接口可以超过12mb /s。 * ssid - 要连接的WiFi网络。 * ssid_key - Wifi网络密码。 * ssid_security - WiFi 安全性。 * ip - Slave IP地址 * port - 将流量路由到的端口。 * mode - 常规或AP接入点模式。 * static_ip - 如果不是 None 则是(IP Address, Subnet Mask, Gateway, DNS Address)的元组 class rpc_wifi_master - WiFi Master Interface ------------------------------------------- 通过WiFi被另一个 ``rpc`` 设备控制。 .. class:: rpc.rpc_wifi_slave(ssid, ssid_key, ssid_security, port=0x1DBA, mode=network.WINC.MODE_STA, static_ip=None) 创建WiFi ``rpc`` 从设备。这个接口可以超过12mb /s。 * ssid - 要连接的WiFi网络。 * ssid_key - Wifi网络密码。 * ssid_security - WiFi 安全性。 * ip - Slave IP地址 * port - 将流量路由到的端口。 * mode - 常规或AP接入点模式。 * static_ip - 如果不是 None 则是(IP Address, Subnet Mask, Gateway, DNS Address)的元组