Share
Embed
PicoRuby's Networking is Incomplete
2025/7/19 @ PicoRuby Overflow Kaigi
https://naniwarb.github.io/picorubyoverflowkaigi/
sylph01
July 19, 2025
Transcript
34 slides
-
PicoRuby's Networking is Incomplete Ryo Kajiwara/ 梶原 龍 (sylph01) 2025/7/19
@ PicoRuby Overflow Kaigi 1 -
何を使ってる? ハードウェア: CYW43439 TCP/IP, TLS: lwIP 暗号ライブラリ: Mbed TLS Pico
SDK はこのへんが入っているだけ でなく、この間の連携をとってくれる 6 -
何を作った (in 2024) ? アプリケーションレベルの暗号処理 TCP Client TLS を使うTCP Client
TCP Client のwrapper としてのHTTP Client 7 -
TCP Client lwIP でTCP Client を実装するには以下のことをする: Protocol Control Block を作る
recv , sent , err , poll のコールバックを作る recv : 受信したデータを扱う sent : 送信したデータを扱う(が何もしない) err : エラーを扱う(がほとんど何もしない) poll : idle な接続を扱う picoruby-net 以下 src/tcp.c 8 -
static tcp_connection_state * TCPClient_new_connection(mrb_state *mrb, const net_request_t *req, net_response_t *res)
{ ... cs->pcb = altcp_new(NULL); altcp_recv(cs->pcb, TCPClient_recv_cb); altcp_sent(cs->pcb, TCPClient_sent_cb); altcp_err(cs->pcb, TCPClient_err_cb); altcp_poll(cs->pcb, TCPClient_poll_cb, 30); altcp_arg(cs->pcb, cs); ... return cs; } https:/ /github.com/picoruby/picoruby/blob/master/mrbgems/picoruby-net/src/tcp.c#L144-L162 9 -
コールバックの例 static err_t TCPClient_recv_cb(void *arg, struct altcp_pcb *pcb, struct pbuf
*pbuf, err_t err) arg に altcp_arg() の第2 引数で指定した値が渡ってくる PicoRuby のTCP 実装ではconnection state の構造体を作ってそれ を引き渡している pcb にはレスポンスに対応するProtocol Control Block が渡ってくる pbuf に受信した内容が入ってくる err には状態が渡ってくる。 ERR_OK でなければエラー 10 -
TLS 対応 lwIP のApplication Layererd TCP(ALTCP) のおかげで少々の変更でTLS 対応可 能。 altcp_new()
の代わりに altcp_tls_new() を呼ぶ altcp_tls_create_config_client() でTLS config を作る それを altcp_tls_new() の引数にする mbedtls_ssl_set_hostname() で接続するホスト名を記述 それ以外はすべて同じ! 11 -
TCP Server の場合は? 一旦TLS は置いとくとして、 altcp_new() するところは同じ altcp_bind() でIP アドレスとポートにbind
する altcp_listen() でlisten altcp_listen_with_backlog() を使うと複数接続をqueue の 形で受け付けることができる 12 -
struct tcp_pcb *pcb = altcp_new(); // bind to 0.0.0.0:3000 err_t
err = altcp_bind(pcb, IPADDR_TYPE_ANY, 3000); cyw43_arch_lwip_begin(); // a new PCB will be created and the original will be deallocated struct tcp_pcb *server_pcb = altcp_listen_with_backlog(pcb, 1); cyw43_arch_lwip_end(); altcp_arg(server_pcb, stuff_to_be_passed); altcp_accept(server_pcb, callback_for_accept); サーバーの初期化はこんな感じ 13 -
static err_t callback_for_accept (void *arg, struct tcp_pcb *client_pcb, err_t err)
{ altcp_arg(client_pcb, stuff_to_be_passed); altcp_sent(client_pcb, callback_for_sent); altcp_recv(client_pcb, callback_for_recv); altcp_poll(client_pcb, callback_for_poll); altcp_err(client_pcb, callback_for_err); } ここまで来ると見覚えありますね。クライアントによるイベントに対応するコールバックを設定していきます 14 -
TCP echo server recv のcallback 内でTCP Client の時と同様pbuf から値を読み込み それを
altcp_write(pcb, buffer_to_be_sent, buffer_size, 0) で書き込み 完了すると sent のcallback が( 一応) 走る 15 -
サーバー側のTLS は面倒 Application Layered TCP のおかげでTLS の追加自体は自明 ただしTLS 自体が面倒なので自明とは言ってない 証明書を持つ必要がある
あと遅くなる。死ぬほど遅くなる これはRubyKaigi 2024 の発表のまとめでも言及した Pico の性能だとRSA 2048bit で6 秒くらいかかる 16 -
だいたいこんなかんじ def respond(request) path = get_path(request) case path when '/'
response = build_response(request.body) else ... end response end 20 -
mrbc_funcall 改め mrbc_send //================================================================ /*! (BETA) Call any method of
the object, but written by C. @param vm pointer to vm. @param v see below example. @param argc see below example. @param recv pointer to receiver. @param method_name method name. @param n_params num of params. >>> but written by C <<< https:/ /github.com/mrubyc/mrubyc/blob/master/src/class.c#L516-L585 22 -
mruby/c ではRuby のメソッドを呼べな い https:/ /github.com/mrubyc/mrubyc/issues/234 The overhead of method
calls is large. Because the Ruby methods will be executed by different VM, cannot pass arguments to the Ruby method. 23 -
BLE ではどうしてる? 2023 年8 月時点でのcommit ではpacket callback をVM code にコンパイルし
て動作させてた。 https:/ /github.com/picoruby/picoruby/commit/e9d98b5efe2bdfa0f532 aa04757c81e105d1bafb mrbc_run_mrblib(packet_callback_vm_code); packet_callback_vm_code = compile("BLE.instance&.packet_callback($_btstack_event_pac ket)"); 24 -
BLE ではどうしてる? 現行ではpolling している。 while true (snip) packet = pop_packet
packet_callback(packet) if packet heartbeat_callback if pop_heartbeat sleep_ms POLLING_UNIT_MS total_timeout_ms += POLLING_UNIT_MS end pop_packet と pop_heartbeat の中身がC 。 https:/ /github.com/picoruby/picoruby/blob/master/mrbgems/picoruby-ble/mrblib/ble.rb#L127-L138 25 -
じゃあTCP server もpolling でなんとか ならない? 知る限りでlwIP でcallback-based じゃないデータのreceive の方法がなさそ う。
https:/ /www.nongnu.org/lwip/2_1_x/group__tcp__raw.html 26 -
mruby/c じゃなくてmruby だった場合 は? send mrb_value mrb_f_send(mrb_state *mrb, mrb_value self)
https:/ /github.com/mruby/mruby/blob/master/src/vm.c#L1073- L1077 mruby-metaprog mrbgem 以下にある C から呼ぶために使うかというとちょっと違う 27 -
mruby/c じゃなくてmruby だった場合 は? mrb_funcall MRB_API mrb_value mrb_funcall(mrb_state *mrb, mrb_value
self, const char *name, mrb_int argc, ...) https:/ /github.com/mruby/mruby/blob/master/src/vm.c#L558- L590 C からRuby コードを呼ぶこと想定だとこっち 28 -
ところで: ESP32 のWiFi 対応? CYW43439 ではない石を使っているのでドライバ部分の流用が不可 能 https:/ /docs.espressif.com/projects/esp-idf/en/stable/esp32/api- guides/wifi.html
cyw43_arch_lwip に依存してる部分がまるごと使えない がESP32 のWiFi チップのドライバ自体は存在してるのでlwIP と WiFi チップの間のやり取りまで面倒を見る必要はない 30 -
ところで: ESP32 のWiFi 対応? lwIP 自体はESP32 のSDK に同梱されているがBSD Sockets API
の利用 を推奨している https:/ /docs.espressif.com/projects/esp-idf/en/stable/esp32/api- guides/lwip.html ということはFreeRTOS task が走る いっそRaspberry Pi Pico W 側ももFreeRTOS に委ねることができ てしまえばBSD Socket API でなんとかできてしまうのでは? 31 -
今後どうする FreeRTOS 経由でlwIP 動かす路線を試してみる メモリ使用量が特に気になっている なんとかなりそうならSocket API で実装 MicroPython はSocket
API 持ってるってことだからもしかしたら FreeRTOS 経由で使ってるのか… ? 32 -
正直なところ HTTP server with TLS だと遅すぎる ちゃんとセキュリティ保とうとするとZero 2 W とBLE
で繋がって、 Zero 2 W がTLS しゃべるほうがよさそう 調べる限りBLE は認証はされるものの通信の秘匿まで必ずある ものではなさそう 33