Rustで組み込みEthernetを試すとき、サンプルコード自体は用意されていても、最初につまずきやすいのは「本当に起動しているのか」「DHCPでIPアドレスを取れているのか」「TCP serverがlistenしているのか」をどう確認するか、という部分だと思います。
この記事では、W55RP20-EVB-PicoでEmbassyのTCP echo server exampleを動かし、RTTやdebug probeをすぐに使えない環境でも、UARTログからDHCP取得状態とTCP接続状態を確認できるようにする流れを整理します。
元になっているのはWIZnet Makersに掲載されているRust networking guideですが、この記事ではQiita向けに「実行手順」と「切り分けポイント」が追いやすい形に再構成しています。
対象読者
- Rustで組み込み開発を試している人
- Embassyのnetworking exampleを動かしてみたい人
- RP2040 + Ethernet controller構成に興味がある人
- RTT/debug probeなしで、UARTログから状態を見たい人
- DHCPやTCP echo serverの動作確認でつまずいている人
この記事でやること
この記事では次の流れで進めます。
- Rust toolchainを用意する
- Embassy repositoryをcloneする
- W55RP20向けTCP echo server exampleをbuildする
- ELFをUF2に変換してboardへ書き込む
- TCP echo serverとして動いているか確認する
- RTTが使えない環境向けに、UARTへ出すと便利なログ位置を整理する
- 参考として、cotton + MACRAW + smoltcp構成の位置づけを整理する
使用した環境
| 項目 | 内容 |
|---|---|
| OS | Windows 11 |
| Board | W55RP20-EVB-Pico |
| Network | Ethernet / DHCP |
| Toolchain | Rust / cargo / rustup |
| Framework | Embassy |
| Optional | USB-UART adapter |
| UART | 115200 baud, 8N1 |
全体構成
PC
├─ USB: UF2書き込み
├─ UART: ログ確認
│
W55RP20-EVB-Pico
├─ RP2040
├─ W5500 Ethernet controller
│
LAN / DHCP router
│
TCP client
この例では、board側でTCP echo serverをport 1234 で待ち受けます。
PC側からTCP接続して文字列を送ると、同じ内容が返ってくる構成です。
Rustをインストールする
Rustが入っていない場合は、Windowsでは次のようにinstallできます。
winget install Rustlang.Rustup
install後、versionを確認します。
rustc --version
cargo --version
rustupとtoolchainも更新しておきます。
rustup self update
rustup update
UF2変換ツールを入れる
RP2040系boardへ書き込むため、ELFをUF2に変換するtoolを入れます。
cargo install elf2uf2-rs
Embassy repositoryをcloneする
git clone https://github.com/embassy-rs/embassy
cd embassy
Embassy repositoryには rust-toolchain.toml が含まれているため、そのrepositoryで使うRust versionが固定される場合があります。
これは意図された挙動です。
W55RP20 exampleの場所
対象exampleは以下です。
embassy/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs
このexampleは、DHCPでIP addressを取得し、port 1234 でTCP echo serverとして動作する構成です。
RP2040 targetを追加する
rustup target add thumbv6m-none-eabi
buildする
embassy/examples/rp に移動してbuildします。
cd embassy/examples/rp
cargo build --release --bin ethernet_w55rp20_tcp_server --target thumbv6m-none-eabi
生成されたbinaryをUF2に変換します。
elf2uf2-rs target/thumbv6m-none-eabi/release/ethernet_w55rp20_tcp_server
環境によって出力されるUF2 fileの場所や名前が異なる場合があるので、変換後のmessageも確認してください。
boardへ書き込む
- BOOTSEL buttonを押したままboardをUSB接続する
- Mass storageとしてmountされたdriveを開く
- 生成された
.uf2fileをdrag & dropする - boardが自動でrebootしてexampleが起動する
まずRTT/logで確認したいこと
Embassyのexampleでは、次のような状態が見えると切り分けがしやすくなります。
Waiting for DHCP...
IP address: 192.168.x.x
Listening on TCP:1234...
Received connection from ...
debug probeやRTTが使える環境であれば、そのまま defmt logを見るのが一番素直です。
ただし、手元の環境によってはRTTやprobe-based debuggingをすぐに使えないことがあります。
その場合は、UARTへ最低限の状態を出しておくと、どこで止まっているのかが分かりやすくなります。
TCP echo serverとして確認する
DHCPで取得したIP addressが分かったら、PC側からTCP接続してみます。
Linux/macOSなら nc を使うと確認しやすいです。
nc 192.168.x.x 1234
接続後、文字列を入力します。
hello
同じ内容が返ってくれば、TCP echo serverとして動いています。
hello
Windowsの場合は、任意のTCP client tool、PowerShell script、またはWSL上の nc などを使って確認できます。
UARTでログを見る
RTTが使えない場合は、USB-UART adapterを使ってUART logを出すと便利です。
配線例は以下です。
| W55RP20-EVB-Pico | USB-UART adapter |
|---|---|
| GP0 / UART0 TX | RX |
| GP1 / UART0 RX | TX |
| GND | GND |
terminal側は以下で開きます。
Baudrate: 115200
Data: 8 bit
Parity: none
Stop bit: 1
Flow control: none
TX/RXは交差接続、GNDは必ず共通にします。
UARTで出したいログ
最低限、以下が見えると切り分けしやすくなります。
| log | 分かること |
|---|---|
start |
firmwareが起動した |
waiting dhcp |
network stack起動後、DHCP待ちに入った |
dhcp ok |
IP addressを取得できた |
listening tcp 1234 |
TCP serverがlisten状態になった |
client connected |
TCP clientから接続が来た |
| remote endpoint | 接続元のIP addressを確認できる |
UARTログを追加する位置
Embassyは更新が速いため、古いdiffをそのまま適用するとbuildできないことがあります。
そのため、ここでは「完全なpatch」ではなく、どのタイミングでUARTへ出すとよいかを整理します。
1. 起動直後
firmwareが起動したことを確認するため、main() の初期化後に出します。
// example / pseudo
uart_write("start\r\n");
2. DHCP待ちに入る前
network stackを起動した後、DHCP待ちに入ったことを出します。
// example / pseudo
uart_write("waiting dhcp\r\n");
3. IP address取得後
DHCPでIP addressを取得できたタイミングで出します。
// example / pseudo
let local_addr = cfg.address.address();
uart_write("dhcp ok\r\n");
uart_write_ip(local_addr);
4. TCP listen前
port 1234 でaccept待ちに入る前に出します。
// example / pseudo
uart_write("listening tcp 1234\r\n");
5. client接続時
clientから接続が来たタイミングで出します。
// example / pseudo
uart_write("client connected\r\n");
uart_write_endpoint(socket.remote_endpoint());
実際には、使用しているEmbassy revisionのUART APIに合わせて Uart の初期化方法やwrite方法を調整してください。
UARTで見たい出力例
最終的に、次のようなlogが見えると動作状態を追いやすくなります。
start
waiting dhcp
dhcp ok
192.168.1.42
listening tcp 1234
client connected
192.168.1.10
ここまで見えれば、少なくとも次のことが分かります。
- firmwareは起動している
- DHCPでIP addressを取得できている
- TCP serverはport
1234で待ち受けている - PC側から接続が来ている
逆に、どこでlogが止まるかを見ることで原因を絞れます。
つまずいた時の確認ポイント
| 症状 | 確認すること |
|---|---|
| UF2 driveが見えない | BOOTSELを押しながらUSB接続しているか |
| buildが失敗する |
rust-toolchain.toml のtoolchainが入っているか |
| target errorが出る |
thumbv6m-none-eabi を追加したか |
| IP addressが出ない | LAN cable、DHCP server、link状態を確認 |
| TCP接続できない | IP address、port 1234、PC側firewallを確認 |
| UART logが出ない | TX/RXの交差、GND共有、baudrate 115200 を確認 |
| 文字化けする | baudrateと8N1設定を確認 |
| 途中で接続が切れる | socket timeout、client側切断、LAN環境を確認 |
MAC addressについて
example内ではMAC addressを設定します。
let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
実験では固定値でも動作確認できますが、同じnetwork上でMAC addressが重複すると通信トラブルの原因になります。
複数台を同じnetworkに接続する場合は、重複しない値を使うようにします。
cotton + MACRAW + smoltcpの位置づけ
ここまでのmain flowでは、Embassy exampleを使ってTCP echo serverを動かしました。
一方で、W5500をMACRAW modeで動かし、上位のIP/TCP処理を smoltcp 側で扱う構成もあります。
cotton projectには、W5500のMACRAW modeや smoltcp integrationを理解するためのexampleが用意されています。
ざっくり整理すると、次のようになります。
| 構成 | 位置づけ |
|---|---|
| Embassy example | まず動かすための実用ルート |
| cotton + MACRAW + smoltcp | Ethernet deviceとnetwork stackの関係を理解するための参考ルート |
MACRAW構成は、この記事のmain手順ではありません。
Rust embedded networkingを下の層から理解したい場合の参考として見るのが良いと思います。
まとめ
W55RP20-EVB-Pico上でRust/EmbassyのTCP echo serverを動かす場合、最初に確認したいのは次の3点です。
- firmwareが起動しているか
- DHCPでIP addressを取得できているか
- TCP serverがport
1234でlistenしているか
RTTやdebug probeが使える場合は、それで状態を見るのが素直です。
一方、すぐにprobe-based debuggingを使えない環境では、UART logを追加しておくと、DHCP取得前で止まっているのか、TCP接続待ちまで進んでいるのかを切り分けやすくなります。
Rust embedded networkingは、toolchain、target、network、debug outputのどこで止まっているかが見えにくいので、まずは「状態が見えるlog」を増やすのが近道でした。




