rpc --- rpc ライブラリ

OpenMV Cam の rpc モジュールを使うと、OpenMV Cam を別のマイクロコントローラやコンピュータに接続し、OpenMV Cam 上でリモートの Python(またはプロシージャ)呼び出しを実行できます。rpc モジュールは逆方向にも対応しており、OpenMV Cam から別のマイクロコントローラやコンピュータ上でリモートプロシージャ(または Python)呼び出しを実行することもできます。

ライブラリの使い方

UART 経由で 1 つのコールバックを公開する最小限の slave:

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.

slave に JPEG フレームを要求する対応した master:

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 を対応する cani2cspi のペアに置き換えてください。

一般に、制御側デバイスで rpc ライブラリを使うには、rpc ライブラリを使ってインターフェースオブジェクトを作成します。例えば次のようになります:

interface = rpc.rpc_uart_master(baudrate=115200)

これにより、rpc の slave と通信するための UART インターフェースが作成されます。

インターフェースを作成したら、あとは次を実行するだけです:

memory_view_object_result = interface.call("remote_function_or_method_name", bytes_object_argument)

すると rpc ライブラリは slave 上でその "remote_function_or_method_name" の実行を試みます。リモートの関数またはメソッドは、最大 2^32-1 バイトまでの bytes_object_argument を受け取ります。リモートメソッドの実行が完了すると、これも最大 2^32-1 バイトまでの memory_view_object_result を返します。引数と応答はどちらも汎用的なバイトコンテナであるため、rpc ライブラリを通じて任意のものを渡し、任意の型の応答を受け取ることができます。引数を渡す簡単な方法は、struct.pack() を使って引数を作成し、もう一方の側で struct.unpack() を使って引数を受け取ることです。応答については、もう一方の側が結果として文字列オブジェクトや json 文字列を送信し、master がそれを解釈できます。

エラーについては、存在しない関数名やメソッド名を実行しようとすると rpc_master.call() メソッドは空の bytes() オブジェクトを返します。rpc ライブラリが slave との通信に失敗した場合、rpc ライブラリは None を返します。

簡潔さを保つため、rpc ライブラリは master デバイスと slave デバイスの間で接続を維持しません。rpc_master.call() メソッドは、slave への接続試行、リモート関数またはメソッドの実行開始、結果の取得を一括して行います。

次に、slave 側では、master と通信するための rpc インターフェースを作成する必要があります。これは次のようになります:

interface = rpc.rpc_uart_slave(baudrate=115200)

これにより、rpc の master と通信するための UART インターフェース層が作成されます。

slave インターフェースを作成したら、master が呼び出せるコールバックをインターフェースオブジェクトに登録する必要があります:

def remote_function_or_method_name(memoryview_object_argument):
    <lots of code>
    return bytes_object_result

interface.register_callback(remote_function_or_method_name)

slave には好きなだけコールバックを登録できます。最後に、コールバックの登録が終わったら、次を実行するだけです:

interface.loop()

これを slave 上で実行すると rpc ライブラリが起動し、master からの待ち受けを開始します。rpc_slave.loop() メソッドは戻らないことに注意してください。

class rpc -- rpc 基底クラス

rpc 基底クラスは rpc_master クラスと rpc_slave クラスによって再実装され、master インターフェースと 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 -- 受信したペイロードごとに 1 回 call_back(data) として呼び出される callable で、datamemoryview です。戻り値は無視されます。

  • queue_depth -- writer が reader を待つ前に送信できる処理中フレームの数です。値を大きくするとスループットが向上しますが、メモリを消費します。

  • 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 ペイロードを返す callable です。

  • write_timeout_ms -- 各ペイロードを送信する際に待機するミリ秒数です。

エラーが発生すると戻ります。キャンセルするには call_back 内で例外を発生させてください。リモート側はタイムアウトします。

class rpc_master -- rpc_master 基底クラス

rpc_master は基底クラスです。トランスポート固有のサブクラス(rpc_can_masterrpc_i2c_masterrpc_spi_masterrpc_uart_master)のいずれかを使用してください。

class rpc.rpc_master

rpc_master オブジェクトを作成します。直接使用することは意図されていません。

call(name: str, data: bytes = bytes(), send_timeout: int = 1000, recv_timeout: int = 1000) memoryview | None

slave デバイス上でリモート呼び出しを実行します。

  • name -- 実行するリモート関数またはメソッドの名前を表す文字列です。

  • data -- リモート関数への引数として渡される bytes 様オブジェクトです。

  • send_timeout -- slave への接続およびリモート関数の実行開始の間に待機するミリ秒数です。master が引数の送信を開始した後は、これは適用されなくなります。ライブラリは引数の転送に最大 5 秒を許容します。

  • recv_timeout -- slave が応答の返送を開始するまで待機するミリ秒数です。master が応答の受信を開始した後は、これは適用されなくなります。ライブラリは応答の転送に最大 5 秒を許容します。

成功時には応答の memoryview を返し、リモート名が slave 上に存在しない場合は空の bytes() を、通信失敗時には None を返します。

class rpc_slave -- rpc_slave 基底クラス

rpc_slave は基底クラスです。トランスポート固有のサブクラス(rpc_can_slaverpc_i2c_slaverpc_spi_slaverpc_uart_slave)のいずれかを使用してください。

class rpc.rpc_slave

rpc_slave オブジェクトを作成します。直接使用することは意図されていません。

register_callback(cb: Callable[[memoryview], bytes | memoryview]) None

master が名前で呼び出せるコールバックを登録します。cb は 1 つの memoryview 引数を取り、bytes 様オブジェクトを返す callable です。コールバックの __name__ がルックアップキーとして使用されます。

schedule_callback(cb: Callable[[], None]) None

cb(引数を取らない callable)を、現在実行中の rpc コールバックが master への応答の返送に成功した直後に 1 回だけ実行されるようスケジュールします。rpc コールバック内から呼び出す必要があります。これにより、長時間実行される処理や rpc.get_bytes/rpc.put_bytes のカットスルー転送を rpc トランザクションの間に実行できます。繰り返し実行が必要な場合は、各呼び出しごとに再登録してください。

setup_loop_callback(cb: Callable[[], None]) None

cb(引数を取らない callable)を、rpc_slave.loop の各反復で呼び出されるよう登録します。rpc_slave.schedule_callback とは異なり、このコールバックは登録されたまま残ります。呼び出しレートは可変であるため、ブロッキングしないようにする必要があります。

loop(recv_timeout: int = 1000, send_timeout: int = 1000) None

rpc slave のディスパッチループを実行します。コールバックから発生した例外による場合を除き、戻りません。

  • recv_timeout -- 再試行する前に master からのコマンドを待機するミリ秒数です。

  • send_timeout -- 受信に戻る前に master が応答を確認応答するまで待機するミリ秒数です。

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 ペリフェラル番号です。

master と slave の message_idbit_rate は一致している必要があります。バスは 120 オームで終端する必要があります。

class rpc_can_slave -- CAN スレーブインターフェース

CAN 経由で別の rpc デバイスから制御されます。

class rpc.rpc_can_slave(message_id: int = 0x7FF, bit_rate: int = 250000, sample_point: float = 75, can_bus: int = 2)

引数の説明については rpc_can_master を参照してください。

class rpc_i2c_master -- I2C マスターインターフェース

I2C 経由で別の rpc デバイスを制御します。

class rpc.rpc_i2c_master(slave_addr: int = 0x12, rate: int = 100000, i2c_bus: int = 2)
  • slave_addr -- slave デバイスの 7 ビット I2C アドレスです。

  • rate -- I2C バスのクロック周波数(Hz)です。

  • i2c_bus -- I2C ペリフェラル番号です。

master と slave のアドレスは一致している必要があります。SCL と SDA には外部プルアップが必要であり、両方のデバイスは共通のグランドを共有する必要があります。

class rpc_i2c_slave -- I2C スレーブインターフェース

I2C 経由で別の rpc デバイスから制御されます。

class rpc.rpc_i2c_slave(slave_addr: int = 0x12, i2c_bus: int = 2)
  • slave_addr -- この slave が応答する 7 ビット I2C アドレスです。

  • i2c_bus -- I2C ペリフェラル番号です。

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 -- SPI バスのクロック周波数(Hz)です。

  • clk_polarity -- アイドル時のクロックレベル(0 または 1)です。

  • clk_phase -- 最初の(0)または 2 番目の(1)クロックエッジでデータをサンプリングします。

  • spi_bus -- SPI ペリフェラル番号です。

master と slave の設定は一致している必要があります。CS、SCLK、MOSI、MISO を直接接続してください。両方のデバイスは共通のグランドを共有する必要があります。

class rpc_spi_slave -- SPI スレーブインターフェース

SPI 経由で別の rpc デバイスから制御されます。

class rpc.rpc_spi_slave(cs_pin: str = 'P3', clk_polarity: int = 1, clk_phase: int = 0, spi_bus: int = 2)
  • cs_pin -- チップセレクト入力ピン名です。

  • clk_polarity -- アイドル時のクロックレベル(0 または 1)です。

  • clk_phase -- 最初の(0)または 2 番目の(1)クロックエッジでデータをサンプリングします。

  • spi_bus -- SPI ペリフェラル番号です。

class rpc_uart_master -- UART マスターインターフェース

非同期シリアル(UART)経由で別の rpc デバイスを制御します。

class rpc.rpc_uart_master(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- シリアルのボーレートです。

  • uart_port -- UART ペリフェラル番号です。

master と slave のボーレートは一致している必要があります。master の TX を slave の RX に、master の RX を slave の TX に接続してください。両方のデバイスは共通のグランドを共有する必要があります。

class rpc_uart_slave -- UART スレーブインターフェース

非同期シリアル(UART)経由で別の rpc デバイスから制御されます。

class rpc.rpc_uart_slave(baudrate: int = 9600, uart_port: int = 3)
  • baudrate -- シリアルのボーレートです。

  • uart_port -- UART ペリフェラル番号です。