rpc — rpc library
The rpc module on the OpenMV Cam allows you to connect your OpenMV Cam to another microcontroller
or computer and execute remote python (or procedure) calls on your OpenMV Cam. The rpc module also
allows for the reverse too if you want your OpenMV Cam to be able to execute remote procedure
(or python) calls on another microcontroller or computer.
How to use the Library
Please checkout the example scripts in OpenMV IDE under Remote Control.
You will need to edit the example code to choose which interface you want to use and to play with the settings the scripts use.
In general, for the controller device to use the rpc library you will create an interface object
using the rpc library. For example:
interface = rpc.rpc_uart_master(baudrate=115200)
This create a UART interface to talk to an rpc slave.
Once the interface is created you just need to do:
memory_view_object_result = interface.call("remote_function_or_method_name", bytes_object_argument)
And the rpc library will try to execute that "remote_function_or_method_name" on the slave. The
remote function or method will receive the bytes_object_argument which can be up to 2^32-1 bytes in
size. Once the remote method finishes executing it will return a memory_view_object_result which
can also be up to 2^32-1 bytes in size. Because the argument and response are both generic byte
containers you can pass anything through the rpc library and receive any type of response. A simple
way to pass arguments is to use struct.pack() to create the argument and struct.unpack() to
receieve the argument on the other side. For the response, the other side may send a string
object or json string as the result which the master can then interpret.
As for errors, if you try to execute a non-existant function or method name the
rpc_master.call() method will return an empty bytes() object. If the rpc library failed to
communicate with the slave the rpc library will return None.
To keep things simple the rpc library doesn’t maintain a connection between the master and slave
devices. The rpc_master.call() method encapsulates trying to connect to the slave, starting execution
of the remote function or method, and getting the result.
Now, on the slave side of things you have to create an rpc interface to communicate with the
master. This looks like:
interface = rpc.rpc_uart_slave(baudrate=115200)
This will create the UART interface layer to talk to an rpc master.
Once you create the slave interface you then need to register call backs that the master can call with the interface object:
def remote_function_or_method_name(memoryview_object_argument):
<lots of code>
return bytes_object_result
interface.register_callback(remote_function_or_method_name)
You may register as many callbacks as you like on the slave. Finally, once you are done registering callbacks you just need to execute:
interface.loop()
On the slave to start the rpc library up and begin listening for the master. Note that the
rpc_slave.loop() method does not return.
class rpc – rpc base class
The rpc base class is reimplemented by the rpc_master and rpc_slave classes to create the
master and slave interfaces. It is not meant to be used directly.
- class rpc.rpc
Creates an
rpcobject. Not meant to be used directly.- get_bytes(buff: bytearray | memoryview, timeout_ms: int) bytes | None
Reimplemented by transport-specific subclasses. Fills
buffwith bytes from the underlying interface withintimeout_msmilliseconds. ReturnsNoneon timeout.
- put_bytes(data: bytes | memoryview, timeout_ms: int) None
Reimplemented by transport-specific subclasses. Sends
dataover the underlying interface withintimeout_msmilliseconds.
- stream_reader(call_back: Callable[[memoryview], None], queue_depth: int = 1, read_timeout_ms: int = 5000) None
Receives a stream of payloads from a remote
rpc.stream_writer. Should be called from inside anrpc_slavecallback (or directly after a successfulrpc_master.call) once both sides have synchronized.call_back– callable invoked once per received payload ascall_back(data)wheredatais amemoryview. Return value is ignored.queue_depth– number of in-flight frames the writer is allowed to send before waiting on the reader. Higher values increase throughput at the cost of memory.read_timeout_ms– milliseconds to wait per payload.
Returns on any error. To cancel, raise an exception inside
call_back; the remote side will timeout.
- stream_writer(call_back: Callable[[], bytes | memoryview], write_timeout_ms: int = 5000) None
Sends a stream of payloads to a remote
rpc.stream_reader. Should be called from inside anrpc_slavecallback (or directly after a successfulrpc_master.call) once both sides have synchronized.call_back– callable invoked with no arguments that returns the nextbytesormemoryviewpayload to send.write_timeout_ms– milliseconds to wait when sending each payload.
Returns on any error. To cancel, raise an exception inside
call_back; the remote side will timeout.
class rpc_master – rpc_master base class
The rpc_master is a base class. Use one of the transport-specific subclasses
(rpc_can_master, rpc_i2c_master, rpc_spi_master, rpc_uart_master).
- class rpc.rpc_master
Creates an
rpc_masterobject. Not meant to be used directly.- call(name: str, data: bytes = bytes(), send_timeout: int = 1000, recv_timeout: int = 1000) memoryview | None
Executes a remote call on the slave device.
name– string name of the remote function or method to execute.data–bytes-like object passed as the argument to the remote function.send_timeout– milliseconds to wait while connecting to the slave and starting execution of the remote function. Once the master begins sending the argument this no longer applies; the library allows up to 5 seconds for the argument transfer.recv_timeout– milliseconds to wait for the slave to begin returning a response. Once the master begins receiving the response this no longer applies; the library allows up to 5 seconds for the response transfer.
Returns a
memoryviewof the response on success, an emptybytes()if the remote name does not exist on the slave, orNoneon communication failure.
class rpc_slave – rpc_slave base class
The rpc_slave is a base class. Use one of the transport-specific subclasses
(rpc_can_slave, rpc_i2c_slave, rpc_spi_slave, rpc_uart_slave).
- class rpc.rpc_slave
Creates an
rpc_slaveobject. Not meant to be used directly.- register_callback(cb: Callable[[memoryview], bytes | memoryview]) None
Registers a callback that the master may invoke by name.
cbis a callable taking onememoryviewargument and returning abytes-like object. The callback’s__name__is used as the lookup key.
- schedule_callback(cb: Callable[[], None]) None
Schedules
cb(a callable taking no arguments) to be executed once, immediately after the currently-running rpc callback successfully returns its response to the master. Must be called from inside an rpc callback. Allows long-running work orrpc.get_bytes/rpc.put_bytescut-through transfers to run between rpc transactions. Re-register on each invocation if repeated execution is required.
- setup_loop_callback(cb: Callable[[], None]) None
Registers
cb(a callable taking no arguments) to be invoked on every iteration ofrpc_slave.loop. Unlikerpc_slave.schedule_callback, this callback remains registered. Must be non-blocking; the call rate is variable.
- loop(recv_timeout: int = 1000, send_timeout: int = 1000) None
Runs the rpc slave dispatch loop. Does not return except by exception raised from a callback.
recv_timeout– milliseconds to wait for a command from the master before retrying.send_timeout– milliseconds to wait for the master to acknowledge the response before returning to receive.
class rpc_can_master – CAN Master Interface
Control another rpc device over CAN.
- class rpc.rpc_can_master(message_id: int = 0x7FF, bit_rate: int = 250000, sample_point: float = 75, can_bus: int = 2)
message_id– 11-bit CAN message id used for data transport.bit_rate– CAN bit rate in bits per second.sample_point– Tseg1/Tseg2 sample-point percentage (e.g. 50.0, 62.5, 75, 87.5).can_bus– CAN peripheral number.
Master and slave
message_idandbit_ratemust match. The bus must be terminated with 120 ohms.
class rpc_can_slave – CAN Slave Interface
Be controlled by another rpc device over CAN.
class rpc_i2c_master – I2C Master Interface
Control another rpc device over I2C.
- class rpc.rpc_i2c_master(slave_addr: int = 0x12, rate: int = 100000, i2c_bus: int = 2)
slave_addr– 7-bit I2C address of the slave device.rate– I2C bus clock frequency in Hz.i2c_bus– I2C peripheral number.
Master and slave addresses must match. External pull-ups are required on SCL and SDA, and both devices must share a common ground.
class rpc_i2c_slave – I2C Slave Interface
Be controlled by another rpc device over I2C.
class rpc_spi_master – SPI Master Interface
Control another rpc device over SPI.
- 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– chip-select pin name.freq– SPI bus clock frequency in Hz.clk_polarity– idle clock level (0 or 1).clk_phase– sample data on the first (0) or second (1) clock edge.spi_bus– SPI peripheral number.
Master and slave settings must match. Connect CS, SCLK, MOSI, MISO directly. Both devices must share a common ground.
class rpc_spi_slave – SPI Slave Interface
Be controlled by another rpc device over SPI.
class rpc_uart_master – UART Master Interface
Control another rpc device over Async Serial (UART).
class rpc_uart_slave – UART Slave Interface
Be controlled by another rpc device over Async Serial (UART).