はじめに

Pico 2 と同じ形の基板 1 枚で、IPv4 でも IPv6 でも有線 LAN に出る。 WIZnet の W6300-EVB-Pico2 は、そのためのボードです。この記事のゴールは、USB と LAN ケーブルをつなぎ、DHCP で IPv4 のアドレスをもらい、PC から ping が通り、TCP でデータを送って同じものが返ってくるところまで。加えて、IPv6 のリンクローカルアドレスが付いていることを確かめ、PC から ping -6 が通るところまでを最初の一歩に含めます。そこへ行く前に確かめたいのは 2 つです。RP2350 と W6300 という別々のチップが、QSPI という 4 本のデータ線でどうつながっているのか。そして W6300 の看板である「IPv4/IPv6 のハードウェア TCP/IP」を、手元のコードが本当に使っているのか。だからこの記事では、内部配線と QSPI のフレームを図で押さえ、W5500 から何が変わったかを整理してから、Arduino と公式 C 例題の 2 つの経路で同じ最初の一歩を通します。

速度の比較、IPv6 の仕組みの深掘り、センサーノードといった応用は、次の記事以降で扱います。ここは基礎だけです。同じ WIZnet の W55RP20-EVB-Pico の基本 と対になる記事で、共通する説明は短くまとめ、この基板で違うところに紙面を使います。

📌 この記事でわかること
  • W6300-EVB-Pico2 は RP2350 と W6300 の 2 チップ構成。W55RP20 のような 1 パッケージ品ではない
  • 2 つは QSPI(データ線 1/2/4 本を切り替えられる SPI)でつながっていて、RP2350 の内蔵 QSPI もハードウェア SPI もこのピンでは使えないので、PIO で QSPI を作っている
  • W6300 は W5500 の後継で、IPv4/IPv6 デュアルスタック・バッファ 2 倍・ソケットを開かずに ping を打てる
  • 最初の一歩は DHCP → ping → TCP エコー、そして IPv6 リンクローカルへの ping -6。両方の経路で手順を示す

🧩 W6300-EVB-Pico2 は何者か

W6300-EVB-Pico2。マイコンの RP2350 と LAN コントローラの W6300 が別々の 2 チップとして載っている。RJ45 は UDE 製で、USB-C の手前に BOOTSEL と RUN の 2 つのボタンが並ぶ

W6300-EVB-Pico2。マイコンの RP2350 と LAN コントローラの W6300 が別々の 2 チップとして載っている。RJ45 は UDE 製で、USB-C の手前に BOOTSEL と RUN の 2 つのボタンが並ぶ

RP2350 と W6300 の 2 チップ構成

W6300-EVB-Pico2 は、Raspberry Pi Pico 2 と同じ外形・同じピン配置の基板に、WIZnet の有線 LAN コントローラ W6300 と RJ45 コネクタを足した評価ボードです。マイコンは Pico 2 と同じ RP2350 です(WIZnet Docs・スイッチサイエンス商品ページ)。

前の記事の W55RP20 は、RP2040 と W5500 を 1 つのパッケージに収めた SiP でした。W6300-EVB-Pico2 はそうではなく、RP2350 と W6300 が基板の上に別々のチップとして載っていて、基板の配線でつながっています。ソフトウェアから見ると「RP2350 の GPIO に W6300 がつながっている」状態で、その点は W55RP20 と同じです。違うのは、つなぎ方が SPI ではなく QSPI だということです。

RP2350 は、Pico 2 で使われているマイコンです。要点だけ挙げると、Arm Cortex-M33 のデュアルコア(RISC-V の Hazard3 コアに切り替えて動かすこともできる)、最大 150 MHz、SRAM 520 KB。OTP(一度だけ書き込めるメモリ)・セキュアブート・Arm TrustZone といったセキュリティ機能もあります。PIO は 3 ブロック・ステートマシン 12 個で、RP2040 の 2 ブロック・8 個から増えています。

項目 仕様(WIZnet Docs・スイッチサイエンス商品ページ)
マイコン RP2350(Cortex-M33 デュアル/Hazard3 デュアルを選択・最大 150 MHz・SRAM 520 KB)
フラッシュ 2 MB(16 Mbit・QSPI 接続・XIP)
Ethernet W6300(10/100BASE-T・PHY 内蔵・オートネゴシエーション)
ハードウェア TCP/IP TCP・UDP・IPv6・IPv4・ICMPv6・ICMPv4・IGMP・MLDv1・ARP・PPPoE
ソケット 8 本を同時に使える。送受信バッファは合計 64 KB
ホストとの接続 QSPI(Single/Dual/Quad)
コネクタ RJ45(PoE 対応)・USB Type-C・3 ピン SWD
PoE 別売の WIZPoE-P1 をスルーホールに付ける(ピンヘッダ・PoE モジュールは付属しない)

スイッチサイエンスでは SKU 10784 として 2025 年 12 月 18 日に発売されています。

Pico 2・W55RP20-EVB-Pico と比べる

外形とピン配置は Pico 2 と同じですが、W6300 につないでいるぶん、外に出ている GPIO は減っています。前の記事の W55RP20-EVB-Pico とも並べます。

Raspberry Pi Pico 2 W6300-EVB-Pico2 W55RP20-EVB-Pico
マイコン RP2350 RP2350 RP2040(パッケージ内)
Ethernet チップ なし W6300(別チップ) W5500(パッケージ内)
マイコンとのつなぎ方 — QSPI(PIO) SPI(PIO)
Ethernet に使う GPIO — GP15〜22 GP20〜25
VBUS 検出 GP24 GP24 GP18
オンボード LED GP25 GP25 GP19
VSYS/3 の測定 GP29(ADC3) GP29(ADC3) GP29(ADC3)
VSYS の範囲 1.8〜5.5 V 4.3〜5.5 V —
USB Micro-USB USB Type-C USB Type-C

ボードの中で使っている GP15〜22 を除くと、汎用 GPIO として使えるのは GP0〜14 と GP26〜28 の 18 本です。Zephyr のボード定義も、ピンヘッダとして定義しているのはこの 18 本だけで、「GP15〜22 は W6300 の SPI/QSPI 用に予約」とコメントしています。

W55RP20-EVB-Pico と違って、オンボード LED は Pico 2 と同じ GP25 です。arduino-pico の定義でも LED_BUILTIN は 25 なので、Pico 2 の LED 点滅のスケッチはピン番号を変えずに使えます。

⚠️ Pico 2 のコードを流用するときの注意:既定の SPI ピンがぶつかる

arduino-pico の既定では、SPI(SPI0)が GP16〜19、SPI1 の MOSI が GP15 に割り当てられています。どちらも W6300 につながっているピンなので、SPI のセンサーや表示器をつなぐときは SPI1 を使い、MOSI を GP11 に移します(SPI1.setTX(11); を SPI1.begin() の前に)。Wire(GP4・5)と Serial1(GP0・1)はそのまま使えます。

40 ピンのうち GPIO と GND 以外の 7 本(VBUS・VSYS・3V3_EN・3V3・ADC_VREF・AGND・RUN)は Pico 2 と同じ意味です。違うのは 2 点です。VSYS に入れてよい電圧が 4.3〜5.5 V で、Pico 2 の 1.8〜5.5 V より狭いこと(乾電池 2〜3 本で VSYS から直接動かす、という Pico 2 の使い方はできません)。そして 3V3_EN を GND に落とすと RP2350 と W6300 の両方の電源が止まることです。動作温度は −20〜85 ℃(自己発熱を含む)、推奨する周囲温度の上限は 70 ℃ です(WIZnet Docs)。


🔗 RP2350 と W6300 はどうつながっているか:QSPI

内部配線:データ線 4 本+CS・SCLK・INT・RST

基板の上で、RP2350 の GPIO15〜22 が W6300 につながっています。ピンの割り当ては、WIZnet Docs のボードページ、公式 C 例題(WIZnet-PICO-C)の wizchip_spi.h、arduino-pico の w6300.h、Zephyr のボード定義で一致しています。

flowchart TD subgraph BRD["W6300-EVB-Pico2(基板の配線)"] P0["RP2350 の PIO
(QSPI を実装)"] G["RP2350 の
GPIO15・GPIO22"] Q["W6300
QSPI ホスト I/F"] IR["W6300
INTn・RSTn"] PHY["W6300
MAC / PHY"] P0 -- "CSn・SCLK
(GPIO16・17)" --> Q P0 <-- "IO0〜IO3
(GPIO18〜21)" --> Q IR -- "INTn → GPIO15" --> G G -- "GPIO22 → RSTn" --> IR Q --> PHY end PHY --- RJ["RJ45"]
RP2350 の GPIO W6300 側 向き
GPIO15 INTn(割り込み) W6300 → RP2350
GPIO16 CSn(チップセレクト) RP2350 → W6300
GPIO17 SCLK(クロック) RP2350 → W6300
GPIO18 IO0(Single では MOSI) 双方向
GPIO19 IO1(Single では MISO) 双方向
GPIO20 IO2 双方向(Quad のときだけ使う)
GPIO21 IO3 双方向(Quad のときだけ使う)
GPIO22 RSTn(リセット) RP2350 → W6300

QSPI の 3 つのモード:変わるのはデータ線の本数

QSPI(Quad SPI)は、SPI のデータ線を 1 本から 2 本・4 本に増やして、1 クロックで運ぶビット数を増やす方式です。W6300 は 3 つのモードを持っていて、どのデータ線を使うかは次のとおりです(WIZnet Docs のボードページ)。

モード 使うデータ線 1 クロックで運ぶビット 向き
Single IO0(MOSI)・IO1(MISO) 1 送りと受けで線が別
Dual IO0・IO1 2 2 本とも双方向
Quad IO0〜IO3 4 4 本とも双方向

Single は普通の SPI と同じで、送る線と受ける線が分かれています。Dual と Quad では、同じ線を送りにも受けにも使います。どのモードでも、CSn と SCLK は 1 本ずつで変わりません。

QSPI のフレーム:命令・アドレス・ダミー・データ

W6300 との 1 回の通信(CSn が Low の間)は、4 つの部分でできています(W6300 データシート v1.0.2 §5.1.1)。

CSn  --+                                                         +--
       +---------------------------------------------------------+
IO    [ Instruction 8bit ][ Address 16bit ][ Dummy 8bit ][ Data N byte ... ]
       |MOD[1:0]|RWB|BSB[4:0]|
部分 中身
Instruction(8 ビット) MOD=どのモードで話すか(00 Single/01 Dual/10 Quad)・RWB=読み 0/書き 1・BSB=どのブロックか(共通レジスタ/ソケット n のレジスタ/送信バッファ/受信バッファ)
Address(16 ビット) そのブロックの中のオフセット
Dummy(8 ビット) 読み出しの前にチップが内部でデータを用意する時間
Data(N バイト) 書き込む値、または読み出した値。続けて読み書きするとアドレスが自動で進む

BSB の割り当ては W5500 と同じで、共通レジスタが 0、ソケット n のレジスタが 1+4n、送信バッファが 2+4n、受信バッファが 3+4n です。W5500 のフレームとの違いは、先頭がアドレスではなく命令になったこと、命令の中にモードのビットが入ったこと、そしてダミーが入ったことです。

面白いのは、命令の 8 ビットだけは、どのモードでも IO0 の 1 本で 1 ビットずつ送る決まりになっていることです。W6300 は命令を受け取ってから、その中の MOD ビットを見て「この先は 4 本で来る」と知ります。モードによって変わるのは、アドレスから後ろのクロック数です。

部分 Single Dual Quad
Instruction 8 クロック 8 クロック 8 クロック
Address 16 クロック 8 クロック 4 クロック
Dummy 8 クロック 4 クロック 2 クロック
Data 1 バイトあたり 8 クロック 4 クロック 2 クロック

フレームの形から数えると、1 KB を読む 1 回の通信は Single で 8,224 クロック、Quad で 2,062 クロックです。同じクロック周波数なら、データ部分はほぼ 4 分の 1 の時間で済む計算になります。クロック周波数そのものは、モードとは別の設定(PIO の分周比)で決まります。

💡 実際の速度は応用編で

WIZnet は、RP2350 と W6300 を Quad モードで動かした自社の iperf3 デモで 91 Mbps を記録したと紹介しています(スイッチサイエンスマガジンの WIZnet 寄稿)。手元の基板で、モードやスタックの違いでどう変わるかは、応用編で測ります。

なぜ PIO で QSPI を作っているのか

RP2350 には、QSPI を話せる回路と SPI を話せる回路がもともと載っています。それでも W6300-EVB-Pico2 は、どちらも使わずに PIO で QSPI を作っています。理由は RP2350 のデータシートとピン表から読み取れます。

1. 内蔵の QSPI(QMI)は、フラッシュ用の専用ピンにしか出せない。 RP2350 の QMI(QSPI Memory Interface)は、外付けのフラッシュや PSRAM からプログラムを直接実行(XIP)するための回路です。QMI の SCK と SD0〜SD3 は、GPIO とは別の 専用 QSPI ピン(Bank 1) にしか割り当てられません。GPIO(Bank 0)側に出せるのは 2 つ目のチップセレクト(CS1n・GPIO0/8/19/47)だけです。しかも専用 QSPI ピンには、基板のフラッシュ(2 MB)がつながっています。

2. ハードウェア SPI はデータ線が 1 本ずつしかない。 RP2350 の SPI0・SPI1 は Arm の PL022 で、扱えるのは Motorola SPI・TI SSI・Microwire の 3 方式です。どれも送り 1 本・受け 1 本で、Dual や Quad は話せません。

3. Single モードでも、ピンが合わない。 Single なら普通の SPI と同じなので、ハードウェア SPI でも話せそうです。ところが、GPIO15〜22 で SPI0 に出せる信号の場所が、配線と合っていません(RP2350 データシートの GPIO 機能表・arduino-pico の SPI.cpp が持つ有効ピン表)。

信号 SPI0 で出せる GPIO(15〜22 の範囲) W6300-EVB-Pico2 の配線 合うか
SCK GPIO18・22 GPIO17 合わない
TX(MOSI) GPIO19 GPIO18(IO0) 合わない
RX(MISO) GPIO16・20 GPIO19(IO1) 合わない
CSn GPIO17・21 GPIO16 (CS は GPIO 操作で代用できる)

そこで PIO のステートマシンで QSPI の波形を作っています。PIO は任意の GPIO に任意のタイミングで信号を出せるので、ピンの割り当てにも、データ線の本数にも縛られません。WIZnet-PICO-C の wizchip_qspi_pio.pio には、Single・Dual・Quad の 3 つの PIO プログラムが並んでいて、違いは out pins, 1/out pins, 2/out pins, 4 の 1 行、つまり 1 クロックで何本のピンに出すかだけです。ちなみに Zephyr のボード定義は、PIO ではなく GPIO を CPU で上げ下げする ビットバング SPI(Single 相当・500 kHz 指定)で W6300 を動かしています。

💡 所感:同じ 4 本線なのに、内蔵 QSPI は使えない

RP2350 自身がフラッシュと QSPI で話しているのに、その回路を W6300 には向けられない。データシートを読むと、QMI は「メモリをアドレス空間に見せる」ための回路で、外付けのフラッシュ・PSRAM 以外の相手を想定していないことがわかります。その隙間を、汎用の PIO が 1 行の違いで埋めている。命令だけは 1 本線で送るという W6300 側の決まりも含めて、ホスト側を PIO で作る前提で設計されたフレームに見えます。

モードの選び方

経路 モードの選び方 既定
公式 C 例題(WIZnet-PICO-C) ルートの CMakeLists.txt の set(WIZCHIP_QSPI_MODE ...) 行 Quad
Arduino(arduino-pico 6.3.1) 選ぶ設定はない(ライブラリのソースで Quad に固定) Quad

WIZnet-PICO-C では、BOARD_NAME が W6300_EVB_PICO2 のときに次の 3 行が並んでいて、使う行のコメントを外します。

    set(WIZCHIP_QSPI_MODE QSPI_QUAD_MODE)
    # set(WIZCHIP_QSPI_MODE QSPI_DUAL_MODE)
    # set(WIZCHIP_QSPI_MODE QSPI_SINGLE_MODE)

この値は、PIO のプログラム(何本のピンに出すか)と、命令の MOD ビットの両方に使われます。片方だけ変わることはないので、モードを変えたら CMake からビルドし直せば足ります。どのモードがどれだけ速いかは、この記事では測りません。 応用編のテーマです。


⚙️ W6300 の中身:W5500 から何が変わったか

要点の再掲:ハードウェア TCP/IP と MACRAW

W6300 も W5500 と同じく、TCP/IP の処理をチップの中の回路で行う ハードウェア TCP/IP(WIZnet の呼び方では TOE)のチップです。マイコンからは 8 本の ソケット が見え、「TCP・ポート 5000 で開く → 待ち受ける → 受信バッファを読む → 送信バッファに書いて送る」とレジスタを読み書きするだけで通信できます。もう 1 つの使い方が MACRAW で、イーサネットのフレームをそのままマイコンに渡し、TCP/IP はマイコン側のソフトウェア(lwIP など)が処理します。

flowchart LR subgraph A["ハードウェアソケット(TOE)"] direction TB A1["アプリ
socket / send / recv"] --> A2["QSPI"] A2 --> A3["W6300 の中で
TCP・UDP・IPv4・IPv6
ARP・ICMP・ICMPv6"] A3 --> A4["MAC / PHY
→ LAN"] end subgraph B["MACRAW + lwIP"] direction TB B1["アプリ
WiFiClient など"] --> B2["RP2350 上の lwIP が
TCP・UDP・IPv4・IPv6
ARP・ICMP・ICMPv6"] B2 --> B3["QSPI(生フレーム)"] B3 --> B4["W6300 は
MAC / PHY だけ
→ LAN"] end A ~~~ B

この 2 つの違いと、どちらの経路でどちらが使われるかは、W55RP20-EVB-Pico の記事 の「ハードウェア TCP/IP(TOE)とは」で詳しく書いています。W6300-EVB-Pico2 でも構図は同じで、Arduino(arduino-pico)は MACRAW+lwIP、公式 C 例題はハードウェアソケットです。

W5500 との比較

W6300 で変わったところを、W55RP20 の中身でもある W5500 と並べます(WIZnet Docs・W6300 データシート v1.0.2)。

項目 W5500 W6300
IP IPv4 のみ IPv4/IPv6 デュアルスタック
ハードウェア処理 TCP・UDP・ICMP・IPv4・ARP・IGMP・PPPoE 左に加えて IPv6・ICMPv6・MLDv1
ホストとの接続 SPI(モード 0/3) QSPI(Single/Dual/Quad)・8 ビットのパラレルバス
ソケット 8 本 8 本
送受信バッファ 送信 16 KB+受信 16 KB=32 KB(既定は各ソケット 2 KB) 送信 32 KB+受信 32 KB=64 KB(既定は各ソケット 4 KB)
ソケットを使わないコマンド なし ARP・PING・ICMPv6(DAD・NS・RS・NA)
IP フラグメント 非対応 非対応
Wake on LAN・パワーダウン あり あり(システムクロックの切り替えも追加)

ソケットのモードにも IPv6 が入りました。TCP と UDP には IPv4 用・IPv6 用に加えて デュアル(TCPD/UDPD)があり、1 本のソケットで IPv4 と IPv6 の両方の接続を受けられます。IP フラグメントに対応していない点は W5500 と同じで、分割された IP パケットは扱えません。

ソケットを開かずに ping を打てる

W6300 には ソケットレスコマンド という仕組みがあります。ソケットを 1 本も開かずに、レジスタに相手のアドレスを書いて命令するだけで、ARP(IPv4)・PING(IPv4)・ARP 相当の近隣探索(IPv6)・PING(IPv6)を W6300 自身が送り、返事が来たかを割り込みで知らせてくれます(W6300 アプリケーションノート「SLC」)。8 本しかないソケットを使わずに、「相手は生きているか」を確かめられるわけです。

IPv6 のアドレスを自動で決めるための手順(自分のアドレスが重複していないかを確かめる DAD、ルーターに設定を問い合わせる RS、自分のアドレスを周りに知らせる NA)も、このソケットレスコマンドで送ります。

デュアルスタックとリンクローカルアドレス

デュアルスタックは、1 つのネットワークインタフェースが IPv4 と IPv6 の両方のアドレスを同時に持ち、どちらでも通信できる状態のことです。家庭の LAN でも、ルーターが IPv6 に対応していれば、PC は IPv4 の 192.168.x.x と IPv6 のアドレスを両方持っています。

この記事で使う IPv6 のアドレスは リンクローカルアドレス です。fe80:: で始まる(fe80::/10)アドレスで、同じリンク(ルーターを越えない範囲=同じスイッチングハブの下)だけで使えます。ルーターから情報(RA)をもらわなくても、機器が自分で作れるのが特徴です。だから、IPv6 のルーターがない LAN でも、PC と基板をつないだだけでリンクローカル同士の通信を試せます。

fe80:0000:0000:0000 : 0208:dcff:fe12:3456
|-- prefix 64bit --|  |-- interface ID 64bit --|
                       MAC 00:08:DC:12:34:56
                       -> 1st byte xor 0x02 = 02
                       -> insert FF:FE in the middle

作り方の定番は、MAC アドレスから EUI-64 形式のインタフェース ID を作る方法です。MAC の先頭バイトの 0x02 のビットを反転し、真ん中に FF:FE を挟んで 64 ビットにし、fe80:: の後ろに付けます。上の例では、MAC 00:08:DC:12:34:56 から fe80::208:dcff:fe12:3456 ができます。作ったアドレスはすぐには使わず、DAD で同じアドレスの機器がいないことを確かめてから使います。

W6300 で大事なのは、チップがリンクローカルアドレスを勝手に作るわけではないことです。W6300 には LLAR(Link Local Address Register)というレジスタがあり、ホストのソフトウェアがアドレスを作って書き込みます。WIZnet のアプリケーションノート「Auto-Configuration」は、MAC から EUI-64 でインタフェース ID を作り、LLAR に書き、ソケットレスコマンドで DAD を行う手順を示しています。

経路 リンクローカルアドレスを作るのは 作り方
Arduino(arduino-pico) RP2350 上の lwIP MAC から EUI-64(lwIP が DAD も行う)
公式 C 例題(WIZnet-PICO-C) 例題のソース 固定値 fe80::208:dcff:fe57:5725 を LLAR に書く

公式 C 例題の固定値は、例題の MAC(00:08:DC:12:34:56)から作った値ではありません。同じ例題を書き込んだ基板を 2 枚同じ LAN につなぐと、MAC も IPv6 のアドレスもぶつかります。


🧭 2 つの使い方と IPv6

Arduino(arduino-pico 6.3.1) 公式 C 例題(WIZnet-PICO-C)
W6300 の使い方 MACRAW(生フレーム) ハードウェアソケット(TOE)
TCP/IP を処理する場所 RP2350 上の lwIP W6300 の中
IPv6 ボードメニューで有効にする(既定は IPv4 のみ) W6300 がハードウェアで処理
QSPI のモード Quad に固定 CMake で選ぶ(既定は Quad)

arduino-pico 6.3.1 の lwIP_w6300 ライブラリは、ヘッダに「original sources: W5500MacRaw」とあり、begin() の中でソケット 0 を MACRAW モードで開き、送受信 32 KB ずつのバッファを全部ソケット 0 に割り当てています。TCP/IP は RP2350 上の lwIP が処理するので、IPv6 が使えるかどうかも lwIP の設定で決まります。arduino-pico では「ツール → IP/Bluetooth Stack」が 既定で「IPv4 Only」 になっていて、「IPv4 + IPv6」を選ぶと LWIP_IPV6=1 でビルドされます。このとき arduino-pico は、起動時に MAC からリンクローカルアドレスを作ります。

公式の C 例題は WIZnet の ioLibrary を使い、W6300 のハードウェアソケットで通信します。IPv6 も W6300 の中で処理されます。


🛠️ 開発環境を整える

手順の大部分は W55RP20-EVB-Pico の記事 の「開発環境を整理する」と同じなので、違うところだけ書きます。

経路 使うもの W55RP20 の記事との違い
(a) Arduino Arduino IDE + arduino-pico 6.3.1 ボードと IP スタックの選択
(b) C/C++ VS Code + Raspberry Pi Pico 拡張 + WIZnet-PICO-C BOARD_NAME は書き換え不要・Import 後に Switch Board で pico2
(c) そのほか MicroPython・CircuitPython・Zephyr 紹介だけ

(a) Arduino IDE + arduino-pico

  1. ボードマネージャで「Raspberry Pi Pico/RP2040/RP2350」(earlephilhower 版)を入れる。この記事では 6.3.1 を使います。WIZnet Maker のガイドは 4.6.0 以降を条件にしています
  2. 「ツール → ボード」で WIZnet W6300-EVB-Pico2 を選ぶ
  3. IPv6 も試すなら、「ツール → IP/Bluetooth Stack」を IPv4 + IPv6 にする
  4. 例題は「ファイル → スケッチ例 → lwIP_w6300 → WiFiClient-W6300」にあります

ライブラリ lwIP_w6300 はコアに同梱されているので、別途入れるものはありません。

(b) VS Code + WIZnet-PICO-C

git clone --recurse-submodules https://github.com/WIZnet-ioNIC/WIZnet-PICO-C.git で取得するところまでは W55RP20 の記事と同じです。違うのは、取得した時点(main・2026-10-06 版)の CMakeLists.txt が 最初から set(BOARD_NAME W6300_EVB_PICO2) になっていることです。BOARD_NAME は書き換えなくてよく、_WIZCHIP_=W6300・PICO_PLATFORM rp2350・QSPI のピン定義と Quad モードが自動で選ばれます。

ただし、VS Code 拡張の Import Project を通すと、そのままではビルドできません。Import は CMakeLists.txt の先頭に set(PICO_BOARD pico CACHE STRING "Board type") を足し(Import の画面にボードを選ぶ欄は無い)、続けて自動で走る CMake の設定(Configure)が次のエラーで止まります。

W6300 用の WIZnet-PICO-C を Import する画面。指定するのは場所と SDK の版(2.3.1)だけで、ボードを選ぶ欄は無い。この後に足される PICO_BOARD は pico になる

W6300 用の WIZnet-PICO-C を Import する画面。指定するのは場所と SDK の版(2.3.1)だけで、ボードを選ぶ欄は無い。この後に足される PICO_BOARD は pico になる

PICO_PLATFORM is specified to be 'rp2350', but PICO_BOARD='pico' uses 'rp2040' which is incompatible. You need to delete the CMake cache or build directory and reconfigure to proceed.

PICO_PLATFORM(RP2350)と PICO_BOARD(RP2040 の基板である pico)が食い違っている、という意味です。厄介なのは、このエラーが出るのは CMake の出力側で、この状態で Compile Project を押してもターミナルには ninja: error: loading 'build.ninja': The system cannot find the file specified. としか出ないことです。build.ninja が無いのは Configure が止まったからで、ninja の行だけ見ても原因はわかりません。

直し方は、拡張のサイドバーの Switch Board で pico2 を選ぶことです。選ぶと「Use RISC-V?」と聞かれるので No(Arm Cortex-M33 で動かす)を選びます。

Switch Board で pico2 を選んだ直後の問い。No で Arm(Cortex-M33)、Yes で RISC-V(Hazard3)向けになる。ここは No

Switch Board で pico2 を選んだ直後の問い。No で Arm(Cortex-M33)、Yes で RISC-V(Hazard3)向けになる。ここは No

これで CMakeLists.txt の行が set(PICO_BOARD pico2 CACHE STRING "Board type") に書き換わり、build フォルダを消さなくても Configure が通りました。Compile Project で全例題が 2,799 ステップでビルドされます(SDK 2.3.1・ビルド種別は拡張の既定の Release)。VS Code 拡張の入れ方と Import の詳しい流れは Pico SDK の環境構築の記事にまとめています。

UF2 の書き込み方(BOOTSEL を押しながら USB をつなぎ、出てきたドライブにコピー)は Pico 2 と同じで、この記事の (b) はこの方法で書き込んでいます。RP2040 と違って RP2350 では、拡張の Run に Zadig のドライバは要りません(picotool の README)。

(c) MicroPython・CircuitPython・Zephyr

WIZnet の MicroPython 例題集(WIZnet-EVB-Pico-micropython-examples)は対応ボードに W6300-EVB-Pico2 を挙げています。Zephyr には w6300_evb_pico2 のボード定義があります。CircuitPython の例題集もありますが、この記事では扱いません。


🚀 最初の一歩:DHCP → ping → TCP エコー → ping -6

家庭のルーターのように DHCP サーバーがある LAN を前提にしています。IPv6 は、ルーターが IPv6 に対応していなくても、リンクローカルだけで試せます。

flowchart TD A["① LAN ケーブルと
USB-C をつなぐ"] --> B["② (a) スケッチを書き込み
IPv4・MAC・IPv6 を見る"] B --> C["③ PC から ping と ping -6"] C --> D["④ (a) TCP エコー
PC から送って返ってくるか"] D --> E["⑤ (b) 公式 C 例題で
dhcp_dns と loopback"] E --> F["⑥ LED を見る"]

① LAN ケーブルと USB-C をつなぐ

ルーターやスイッチングハブから LAN ケーブルを RJ45 に、PC から USB-C をつなぎます。電源は USB から取ります。PC も同じルーター・ハブの下にいることを確かめておきます(リンクローカルアドレスはルーターを越えません)。

LAN を挿すと RJ45 の緑が点灯し、オレンジが通信のたびに点滅します。

② DHCP で IPv4 を取り、MAC と IPv6 も表示する(Arduino)

W55RP20 の記事のスケッチを W6300 に移し、IPv6 のアドレスも表示するようにしました。DHCP で IPv4 をもらって表示し、IPv6 のアドレスを一覧し、TCP 5000 番で受けたデータをそのまま返します。 ④の TCP エコーも、このスケッチ 1 本で試せます。

// W6300-EVB-Pico2: DHCP で IPv4 を取り、IPv6 のアドレスも表示して、TCP 5000 番でエコーする(arduino-pico 6.3.1)
// ツール → IP/Bluetooth Stack を「IPv4 + IPv6」にすると IPv6 が有効になる
#include <W6300lwIP.h>
#include <lwip/netif.h>

Wiznet6300lwIP eth(16);     // CS = GPIO16(基板の内部配線)
WiFiServer server(5000);    // TCP エコー用のポート

void printIPv6() {
#if LWIP_IPV6
  netif *n = eth.getNetIf();
  for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
    u8_t st = netif_ip6_addr_state(n, i);
    if (ip6_addr_isinvalid(st)) continue;  // 空きスロットは飛ばす
    Serial.printf("IPv6: %s (%s)\n", ip6addr_ntoa(netif_ip6_addr(n, i)),
                  ip6_addr_ispreferred(st) ? "preferred" : "tentative");
  }
#else
  Serial.println("IPv6: off (Tools > IP/Bluetooth Stack > IPv4 + IPv6)");
#endif
}

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 5000) {}  // USB シリアルを開くまで最大 5 秒待つ

  if (!eth.begin()) {                    // MAC 省略 = チップ固有 ID から生成
    Serial.println("W6300 not found");
    while (true) delay(1000);
  }
  Serial.print("DHCP");
  while (!eth.connected()) {             // IPv4 が付くまで待つ
    Serial.print('.');
    delay(500);
  }
  Serial.println();

  uint8_t mac[6];
  eth.macAddress(mac);
  Serial.printf("MAC : %02X:%02X:%02X:%02X:%02X:%02X\n",
                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Serial.print("IP  : "); Serial.println(eth.localIP());
  Serial.print("MASK: "); Serial.println(eth.subnetMask());
  Serial.print("GW  : "); Serial.println(eth.gatewayIP());

  delay(3000);                           // IPv6 の重複確認(DAD)が終わるのを待つ
  printIPv6();

  server.begin();
  Serial.println("TCP echo server on port 5000");
}

void loop() {
  WiFiClient client = server.accept();
  if (!client) return;
  Serial.print("client: "); Serial.println(client.remoteIP());
  while (client.connected()) {
    int n = client.available();
    if (n > 0) {
      uint8_t buf[128];
      n = client.read(buf, min(n, (int)sizeof(buf)));
      client.write(buf, n);              // 受け取った分をまとめて返す
    }
    delay(50);                           // lwIP のポーリング(既定 20 ms)が走る間を空ける
  }
  client.stop();
  Serial.println("closed");
}

このスケッチと④の PowerShell は、GitHub の ramtuc/wiznet-pico-basics(MIT ライセンス)に置いています。スケッチは arduino/w6300-evb-pico2/dhcp_tcp_echo_ipv6/、PowerShell は tools/tcp_echo_client.ps1 です。

W55RP20 版からの変更は、ヘッダ(W6300lwIP.h)とクラス名(Wiznet6300lwIP)、CS のピン番号(16)、そして IPv6 を表示する printIPv6() です。loop() の delay(50) は W55RP20 版と同じく必要で、理由は④の後で説明します。IPv6 のアドレスは lwIP のネットワークインタフェースから直接読み、DAD が終わって使える状態(preferred)か、確認中(tentative)かも一緒に出しています。

ボード「WIZnet W6300-EVB-Pico2」、IP/Bluetooth Stack「IPv4 + IPv6」で書き込み、シリアルモニタを 115200 bps で開くと、次のように出ました。最初の 2 行は lwIP_w6300 ライブラリ自身が出すもので、0x61 は W6300 のチップ ID レジスタ(CIDR0)の値です。W6300 データシートでは、IPv6 対応の W6x00 系の ID として 0x6100 に固定されています。

version = 0x61
MAC RAW mode!
DHCP..
MAC : D6:C7:62:4C:A0:A5
IP  : 192.168.0.74
MASK: 255.255.255.0
GW  : 192.168.0.1
IPv6: FE80::D4C7:62FF:FE4C:A0A5 (preferred)
TCP echo server on port 5000

IP・ゲートウェイは筆者の LAN の値、MAC はこの基板の値です。MAC は基板ごとに違い、2 文字目の 6 はローカル管理のアドレスであることを示しています(下の囲み)。LAN ケーブルを挿した状態で起動すれば、DHCP は数秒で取れます。リセットしても MAC・IP・IPv6 は同じ値でした。

IPv6 の行は、MAC から EUI-64 で作ったリンクローカルアドレスです(lwIP は大文字で表示します)。MAC の先頭バイト D6 の 0x02 を反転すると D4、真ん中に FF:FE を挟むと FE80::D4C7:62FF:FE4C:A0A5 になり、表示と一致します。DAD を待つ 3 秒のうちに (preferred)(使える状態)になっています。

ルーターが IPv6 の RA を出している LAN では、FD(ULA)や 2001 で始まるアドレスの行が、リンクローカルの次に出ます(lwIP の既定でアドレスの自動設定が有効なため)。ただし RA はルーターが周期的に送るものなので、DHCP の直後 3 秒で出す表示には間に合わないことがあります。筆者の LAN では起動直後の表示には出ず、起動から約 2 分後の表示で FD38:A8E8:B9B7:C54C:D4C7:62FF:FE4C:A0A5 (preferred) の行が増えました。後ろ 64 ビットは、リンクローカルと同じ EUI-64 のインタフェース ID です。

「IPv4 Only」(既定)のまま書き込んだときは、IPv6 の行が IPv6: off (Tools > IP/Bluetooth Stack > IPv4 + IPv6) になります。

💡 MAC アドレスはどう決まるか

Arduino(arduino-pico): eth.begin() に MAC を渡さないと、RP2350 の固有 ID から 6 バイトを作り、先頭バイトを「ローカル管理・ユニキャスト」に設定します。基板ごとに違う値になり、書き込み直しても変わりません。決まった値にしたいときは eth.begin(mac) で渡します。

公式 C 例題(WIZnet-PICO-C): ソースに 00:08:DC:12:34:56 が書いてあります。00:08:DC は WIZnet のベンダー番号です。

③ PC から ping と ping -6

まず IPv4 です。シリアルに出た IP に、PC から ping を打ちます。

ping 192.168.x.x

10 発打って損失 0、RTT は最小 2 ms・平均 12 ms・最大 34 ms、TTL は 255 でした。⑤の公式 C 例題で同じ ping を打つと、数字がはっきり変わります。

経路 応えている場所 RTT(最小/平均/最大) TTL
(a) Arduino(MACRAW + lwIP) RP2350 上の lwIP 2/12/34 ms 255
(b) 公式 C 例題(ハードウェアソケット) W6300 の回路 10 発すべて 1 ms 未満 128

(a) の RTT の幅は、④の後で説明する lwIP の 20 ms ごとのポーリングのためと考えられます。ping が W6300 に届いても、RP2350 が次にポーリングするまで返事は作られません。(b) では W6300 がその場で返事を作ります。TTL の 255 は lwIP の既定値(IP_DEFAULT_TTL)、128 は W6300 が返した値で、Windows の ping は返事の TTL を表示するので、どちらが応えているかを TTL で見分けられます。この傾向は W55RP20-EVB-Pico と同じです。

次に IPv6 です。リンクローカルアドレスに ping を打つときは、アドレスの後ろに % と PC 側のインタフェースを付けます。これを ゾーン ID と呼びます。リンクローカルアドレスはどのリンクでも同じ fe80:: で始まるので、PC に LAN の口が複数あると、どの口から送ればよいか決められないからです。

PC の OS インタフェースの調べ方 ping の書き方
Windows ipconfig で有線 LAN の「リンクローカル IPv6 アドレス」を見る。末尾の %12 などの数字がインタフェース番号 ping -6 fe80::xxxx%12
Linux ip -6 addr でインタフェース名(eth0・enp3s0 など)を見る ping -6 fe80::xxxx%eth0
macOS ifconfig でインタフェース名(en0 など)を見る ping6 fe80::xxxx%en0

fe80::xxxx の部分には、シリアルに出た基板の IPv6 アドレスを入れます。番号やインタフェース名は PC 側 のものです。基板側のものではありません。

前提は、PC の有線 LAN アダプタで IPv6 が有効になっていることです。Windows ではアダプタのプロパティの「インターネット プロトコル バージョン 6(TCP/IPv6)」のチェックで、これが外れていると ipconfig にリンクローカル IPv6 アドレスが出ず、ping -6 は PING: transmit failed. General failure. で終わります。

返事をしているのは、Arduino の経路では RP2350 上の lwIP です。W6300 は生フレームを渡しているだけです。⑤の公式 C 例題では、同じ ping に W6300 の中の回路が応えます。

④ TCP エコー(Arduino)

PC から TCP 5000 番につないで文字を送り、同じ文字が返ってくれば TCP の往復ができています。Windows なら、次を tcp_echo_client.ps1 という名前で保存して PowerShell から実行します。ファイルは UTF-8(BOM 付き)で保存してください。 Windows PowerShell 5.1 は BOM なしの UTF-8 を cp932 として読むため、日本語コメントの次の行が消え、接続できているのに返事が空になります(PowerShell 7 では起きません)。

# TCP エコーの確認 (Windows PowerShell 5.1 / PowerShell 7)
# 使い方: .\tcp_echo_client.ps1 192.168.x.x "hello W6300"
# このファイルは UTF-8 (BOM 付き) で保存してある。BOM なしで保存し直すと Windows PowerShell 5.1 が
# 日本語コメントを cp932 として読み、コメントの直後の行が消えて返事が空になる (PowerShell 7 は問題なし)
param(
  [string]$ip = "192.168.x.x",            # シリアルに出た IP (W55RP20 / W6300)
  [string]$text = "hello"                 # 送る文字列 (記事では hello W55RP20 / hello W6300)
)
$c = New-Object System.Net.Sockets.TcpClient($ip, 5000)
$s = $c.GetStream()
$s.ReadTimeout = 3000                     # 3 秒返事がなければエラーで止める (ms)
$msg = [Text.Encoding]::ASCII.GetBytes("$text`n")
$s.Write($msg, 0, $msg.Length)
$buf = New-Object byte[] 64
$got = ""
while ($got -notmatch "`n") {             # 改行が返ってくるまで読み続ける (loop)
  $n = $s.Read($buf, 0, $buf.Length)
  if ($n -le 0) { break }                 # 相手が接続を切ったら終わり (EOF)
  $got += [Text.Encoding]::ASCII.GetString($buf, 0, $n)
}
$got
$c.Close()

日本語コメントの末尾を (ms)・(loop) のような ASCII で終えているのも同じ理由で、BOM なしで保存されてしまっても行が消えないようにしています。VS Code なら右下のエンコード表示から「UTF-8 with BOM」で保存し直せます。

実行は、IP と送る文字列を引数で渡します(IP はシリアルに出た値)。

.\tcp_echo_client.ps1 192.168.x.x "hello W6300"

スクリプトの実行が実行ポリシーで止められる場合は、powershell -ExecutionPolicy Bypass -File .\tcp_echo_client.ps1 192.168.x.x "hello W6300" の形にすると、その 1 回だけ実行できます。

hello W6300 と表示されれば成功です。筆者の環境では hello W6300 1 が 19 ms、続けて実行した hello W6300 2 が 22 ms で返り、その後の ping も通りました。スクリプトが最後に $c.Close() で切ったあと、シリアルに client: <PC の IP> に続いて closed が出れば、切断まで処理できています。Linux や macOS なら nc 192.168.x.x 5000 でつなぎ、打った行がそのまま返ってくるかを見ます。

Read() を 1 回呼ぶだけにしないのは、TCP には「1 回の送信=1 回の受信」という区切りがないからです。返事が 2 つのパケットに分かれて届くと、1 回目の Read() は先に着いた分だけを返すので、行の終わり(改行)が来るまで読み続けます。読み残しがあるまま Close() すると、Windows は普通の切断(FIN)ではなく RST(強制切断)を送ります。

⚠️ なぜ delay(50) が要るのか:lwIP は 20 ms ごとのポーリングで動く

arduino-pico の有線 Ethernet は、W6300 でも W55RP20 でも共通の仕組み(lwIP_Ethernet)で、lwIP を 20 ms 周期のポーリングで動かしています。ドキュメント(docs/ethernet.rst)には「By default it will run every 20ms」とあり、libraries/lwIP_Ethernet/src/LwipEthernet.cpp にも _pollingPeriod = 20 があります。

落とし穴は、ユーザーのコードが lwIP を呼ぶたびに、このポーリングが先送りされることです。同じファイルのコメントに「It causes ethernet_timeout_reached to not be called when user code does frequent lwip calls」とあります。client.connected() や client.available() も lwIP の呼び出しなので、これを休みなく呼ぶループでは、受信の処理(相手からの切断・ARP・ping)が走りません。W55RP20-EVB-Pico では、while (client.available()) client.write(client.read()); を休みなく回すと最初の 1 バイトしか返らず、PC が切っても closed が出ず、その間は ping にも応えなくなりました。

ループの 1 周ごとにポーリング周期より長く休む delay(50) を入れ、受け取った分をまとめて write() します。WiFiClient は Nagle アルゴリズムが既定で有効なので(WiFiClient.cpp の defaultNoDelay = false)、1 バイトずつ書くと先の 1 バイトの ACK を待って次を溜めることになり、PC 側の遅延 ACK と重なると待ち合いになります。ポーリングの周期は lwipPollingPeriod(ms) で変えられますが、短くしすぎると固まる、とドキュメントにあります。

1 行受けて client.println() で返し、client.stop() でサーバー側から切る 1 往復型にしても、ループで lwIP を呼び続けないので同じ問題は起きません。その代わり、1 回の接続で送れるのは 1 行だけです。

⑤ 同じことを公式 C 例題で(ハードウェアソケット)

今度は W6300 のハードウェアソケットで同じことをします。WIZnet-PICO-C の 2 つの例題を順に使います。どちらも起動時に QSPI のモード([QSPI QUAD MODE])とクロックを表示し、W6300 のチップ ID を読んで、合わなければ ACCESS ERR を出して止まります。出力は USB シリアルに出る設定です。

dhcp_dns(examples/dhcp_dns/wizchip_dhcp_dns.c)は、DHCP で IPv4 をもらって表示し、www.wiznet.io の IP を DNS で引きます。取得した時点のソースは .dhcp = NETINFO_DHCP なので、そのままビルドして書き込みます。DHCP に成功すると DHCP success と、ネットワークの設定が表示されます。W6x00 向けの表示には、IPv4 の項目に加えて LLA(リンクローカルアドレス)・GUA・GW6・DNS6 の行があります。

W6300 用 dhcp_dns の出力(シリアル端末は Tera Term)。先頭の [QSPI QUAD MODE] が Quad で話していることを示し、IPv4 の設定の下に GW6・LLA・GUA・SUB6・DNS6 の IPv6 の 5 行が並ぶ。192.168.0.x は筆者の LAN の値

W6300 用 dhcp_dns の出力(シリアル端末は Tera Term)。先頭の [QSPI QUAD MODE] が Quad で話していることを示し、IPv4 の設定の下に GW6・LLA・GUA・SUB6・DNS6 の IPv6 の 5 行が並ぶ。192.168.0.x は筆者の LAN の値

見るところは、IPv4 の 5 行の下に並ぶ IPv6 の 5 行です。LLA は FE80:0000:0000:0000:0208:DCFF:FE57:5725 で、上で見たソースの固定値がそのまま W6300 に書かれています。DNS6 の 2001:4860:4860::8888(表示は 0 を省かない形)も例題に固定で入っている値で、Google Public DNS のアドレスです。GUA と GW6 は 0 のままでした。

IPv4 の側は、MAC が例題に固定で入っている 00:08:DC:12:34:56、IP は DHCP でもらった 192.168.0.110(筆者の LAN の値)、リースは 7200 秒です。この MAC は W55RP20-EVB-Pico 用の例題と同じなので、両方の基板を同じ LAN で動かすとルーターは同じ IP を貸します(筆者の LAN でも W55RP20 のときと同じ 192.168.0.110 でした)。DNS は www.wiznet.io を 183.111.138.249 に引けています。

起動時のクロックの行(この図では画面の外)は、下の loopback の図に写っている [PIO QSPI CLOCK SPEED : 37.50 MHz] (sys=150.00 MHz) です。RP2350 を既定の 150 MHz で動かし、その 1/4 で PIO の QSPI を回しています。W55RP20 の例題は RP2040 を 200 MHz に上げて PIO の SPI を 50 MHz で回していたので、クロックだけなら W6300 のほうが低い設定です。その代わりデータ線が 4 本あるので、1 クロックで運べるビットは 4 倍になります。

loopback(examples/loopback/wizchip_loopback.c)は、TCP 5000 番で受けたデータを返すサーバーです。こちらは 固定 IP(既定は 192.168.11.2)なので、自分の LAN に合わせて書き換えてからビルドします。リンクローカルアドレスも同じ構造体に書いてあります。

static wiz_NetInfo g_net_info = {
    .mac = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56}, // MAC address
    .ip = {192, 168, 11, 2},                     // ← LAN 内の空いている IP に
    .sn = {255, 255, 255, 0},                    // Subnet Mask
    .gw = {192, 168, 11, 1},                     // ← ルーターの IP に
    .dns = {8, 8, 8, 8},                         // DNS server
    .lla = {
        0xfe, 0x80, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x02, 0x08, 0xdc, 0xff,
        0xfe, 0x57, 0x57, 0x25
    },             // Link Local Address
    // ...
};

ここでは筆者の LAN に合わせて、IP を 192.168.0.250、ゲートウェイを 192.168.0.1 に書き換えました(どちらも筆者の LAN の値です)。

W6300 用 loopback の起動表示。2 行目が PIO で作った QSPI のクロック(37.50 MHz・sys 150 MHz)、最後の行が TCP 5000 番で待ち受けを始めたソケット 0 の状態

W6300 用 loopback の起動表示。2 行目が PIO で作った QSPI のクロック(37.50 MHz・sys 150 MHz)、最後の行が TCP 5000 番で待ち受けを始めたソケット 0 の状態

書き込んだら、④と同じ PowerShell(IP の引数を固定 IP に変える)で 5000 番に送ります。hello W6300 1・hello W6300 2 とも同じ文字列が返り、送ってから返るまでは約 10 ms でした。ping も 10 発すべて応答があり、RTT は 1 ms 以下・TTL 128 で、ここで応えているのは W6300 の中の回路です。

W6300 版の loopback は、ソケットの状態を 0:Listen, TCP server loopback, port [5000] as IPv4 mode の 1 行で知らせます。起動時に 1 行出て、PC から 1 回つないで切るたびに同じ行がもう 1 行増えます(接続中の行は出ません)。W55RP20 版の loopback は、接続が来てもシリアルに何も出しませんでした。

IPv6 でも確かめるなら、表示された LLA に ③と同じ書き方で ping -6 を打ちます(既定なら fe80::208:dcff:fe57:5725%<PC 側の番号>)。PC の有線 LAN アダプタで IPv6 が有効になっていることが前提です。

loopback の既定は IPv4 の TCP サーバーだけが有効で、ソースの #define IPV6 を有効にすると IPv6 のサーバー(5004 番)や、IPv4 と IPv6 の両方を 1 本のソケットで受けるデュアルのサーバー(5008 番)も動きます。ここから先は応用編で扱います。

⑥ LED を見る

RJ45 の LED の点き方は①のとおりです。W6300 は全二重/半二重・リンク・速度(10/100)・アクティビティの LED 出力を持っていて、ユーザー LED は別にある GPIO25 の LED です。


💡 こういうことにも使える(応用編の予告)

  • IPv6 の LAN の機器: IPv6 だけのネットワークや、ULA(fd で始まる、LAN の中だけで使う IPv6 アドレス)で機器を管理する構成の端末にする
  • QSPI の速さを活かすデータロガー: ADC の値や画像を、有線で途切れずに PC へ流す
  • 有線のセンサーノード・小さな Web サーバー: W55RP20 の記事と同じ用途。RP2350 の SRAM と CPU に余裕があるぶん、アプリ側にできることが増える
  • PoE 給電の機器: WIZPoE-P1 を付けて LAN ケーブル 1 本で電源も取る

🔧 トラブルシューティング

症状 見るところ 試すこと
RJ45 の LED が点かない ケーブル・ハブのポート 別のケーブル・ポートで試す。PC で同じケーブルがリンクするか確かめる
DHCP の点が止まらない ルーターの DHCP・リンク LED 固定 IP で試す(Arduino は eth.config(IPAddress(192,168,1,50), IPAddress(192,168,1,1), IPAddress(255,255,255,0)); を begin() の前に)
LAN を挿さずに起動したら DHCP.... が続く lwIP の DHCP の再送間隔 後から挿せば再起動なしで取れる。再送の間隔が 1→2→4→8→16→32→60 秒と延びていくので、次の再送まで待つ(筆者の例では挿してから約 40 秒)
W6300 not found / ACCESS ERR ボードの選択・BOARD_NAME Arduino はボード「WIZnet W6300-EVB-Pico2」、C 例題は BOARD_NAME W6300_EVB_PICO2 になっているか
IPv6: off と出る Arduino の IP スタック 「ツール → IP/Bluetooth Stack」を「IPv4 + IPv6」にして書き込み直す
ping -6 が「宛先に到達できない」 ゾーン ID・PC の IPv6 % の後ろが PC 側 の有線 LAN の番号・名前か。PC と基板が同じハブの下か
ping -6 が PING: transmit failed. General failure. PC 側アダプタの IPv6 有線 LAN アダプタの「インターネット プロトコル バージョン 6(TCP/IPv6)」が無効。チェックを入れ、ipconfig にリンクローカル IPv6 アドレスが出るか確かめる
IPv6 の行が tentative のまま DAD の待ち時間・同じアドレスの機器 delay(3000) を延ばす。同じ MAC・同じ LLA の機器(公式 C 例題を書き込んだ別の基板など)が同じ LAN にないか
Import 後の Compile で ninja: error: loading 'build.ninja' CMake の出力(Configure) PICO_PLATFORM is specified to be 'rp2350', but PICO_BOARD='pico' ... で止まっている。Switch Board で pico2 を選び、Use RISC-V? は No
QSPI のモードを変えたら ACCESS ERR CMake のキャッシュ build フォルダを消して CMake の構成からやり直す。WIZCHIP_QSPI_MODE の行が 1 つだけ有効か
ping は通るが TCP が返らない PC のファイアウォール・ポート番号 ポート 5000 で合っているか。同じ IP の機器が他にないか(MAC・IP の重複)
エコーの途中から ping も通らなくなる loop() の受信ループ ループが休まず lwIP を呼び続けている。1 周ごとに delay(50) を入れる(④の後の解説)
PowerShell の返事が空行だけ(接続も ping も通る) .ps1 の保存形式 BOM なしの UTF-8 だと Windows PowerShell 5.1 が日本語コメントの次の行を落とす。UTF-8(BOM 付き)で保存し直すか、PowerShell 7 で実行する
PowerShell に最初の数文字しか出ない PC 側の読み方 Read() 1 回で終えていないか。改行が来るまで読むループにする
SPI のセンサーをつないだら LAN が止まる SPI のピン 既定の SPI(GP16〜19)や SPI1 の MOSI(GP15)を使っていないか。SPI1.setTX(11); で移す
BOOTSEL でドライブが出ない USB ケーブル 充電専用ケーブルでないか。BOOTSEL を押したまま USB を挿す

✅ まとめ

  • W6300-EVB-Pico2 は、Pico 2 と同じ形の基板に RP2350 と W6300 を別々のチップとして載せたボード。外に出る GPIO は GP0〜14・GP26〜28 の 18 本で、LED は Pico 2 と同じ GP25。VSYS は 4.3〜5.5 V
  • RP2350 と W6300 は QSPI(INT 15・CS 16・SCLK 17・IO0〜3 = 18〜21・RST 22)でつながる。内蔵の QMI はフラッシュ専用のピンにしか出せず、ハードウェア SPI は 1 本線でピンも合わないので、PIO で QSPI を作っている
  • QSPI の Single/Dual/Quad で変わるのは、アドレスから後ろを何本のデータ線で送るか。命令の 8 ビットは常に 1 本線で、その中のビットでモードを伝える
  • W6300 は W5500 の後継で、IPv4/IPv6 デュアルスタック・バッファ 64 KB・ソケットレスコマンドが加わった。リンクローカルアドレスはチップではなくホストのソフトウェアが作る
  • 最初の一歩は DHCP → ping → TCP エコー → ping -6。Arduino(MACRAW+lwIP)と公式 C 例題(ハードウェアソケット)の両方で同じ確認ができる

次の一手は、この基板でしか試せないことを測ることです。QSPI の 3 つのモードとクロックで転送の速さがどう変わるか、ハードウェアソケットと lwIP でどう違うか、そして IPv6 のアドレスを自動で取るところまでを、応用編で扱います。


関連記事


参考