# WiFiスタックとSDカードファイルシステム

> wifi/withwifi.c の CYW43 + lwIP + mbedTLS 統合、socket.c の TCP クライアント/サーバ/TLS API、wifi/picow_ntp_client.c の NTP 同期、file.c のファイル操作ステートメント群、interface/ff.c (FatFs) と interface/pico-sdmm.c (SPI MMC ドライバ)、withoutwifi.c のスタブ切り替えを解説します。

- Repository: machikania/phyllosoma
- GitHub: https://github.com/machikania/phyllosoma
- Human wiki: https://grok-wiki.com/public/wiki/machikania-phyllosoma-fab169c4381c
- Complete Markdown: https://grok-wiki.com/public/wiki/machikania-phyllosoma-fab169c4381c/llms-full.txt

## Source Files

- `file.c`
- `wifi/withwifi.c`
- `wifi/socket.c`
- `wifi/wifi.h`
- `interface/ff.c`
- `interface/pico-sdmm.c`
- `wifi/withoutwifi.c`

---

<details>
<summary>関連するソースファイル</summary>
このWikiページの生成に使用したファイル:

- [wifi/withwifi.c](wifi/withwifi.c)
- [wifi/withoutwifi.c](wifi/withoutwifi.c)
- [wifi/socket.c](wifi/socket.c)
- [wifi/wifi.h](wifi/wifi.h)
- [wifi/picow_ntp_client.c](wifi/picow_ntp_client.c)
- [wifi/picow_tcp_client.c](wifi/picow_tcp_client.c)
- [wifi/picow_tcp_server.c](wifi/picow_tcp_server.c)
- [wifi/picow_tls_client.c](wifi/picow_tls_client.c)
- [wifi/wifierror.c](wifi/wifierror.c)
- [file.c](file.c)
- [interface/ff.c](interface/ff.c)
- [interface/pico-sdmm.c](interface/pico-sdmm.c)
</details>

# WiFiスタックとSDカードファイルシステム

本ページでは、Machikania Phyllosomaにおけるネットワーク接続とストレージの2大サブシステムを解説します。WiFi側はCYW43チップ・lwIP・mbedTLSの三層統合であり、BASICインタープリタからTCPクライアント／サーバ／TLS接続・NTP時刻同期を透過的に利用できるAPIを提供します。ストレージ側はFatFs (R0.14b) をハードウェアSPIで駆動するSDカードドライバを中心に、BASICステートメントへのファイル操作マッピングが完結しています。

Pico WとPico（非WiFiモデル）を単一のコードベースでサポートするため、`wifi/withwifi.c` と `wifi/withoutwifi.c` がビルド対象に応じて切り替えられるスタブ分離構造を採用しています。

---

## アーキテクチャ概観

```text
┌─────────────────────────────────────────────────────────────┐
│  KM-BASIC インタープリタ（BASICステートメント／関数）         │
│  TCPCLIENT / TLSCLIENT / TCPSERVER / TCPSEND / NTP など      │
│  FOPEN / FCLOSE / FGET / FPUT / FFIND / SETDIR など         │
└──────────────┬──────────────────────────┬────────────────────┘
               │ lib_wifi()               │ lib_file() / lib_fopen()
 ┌─────────────▼──────────────┐  ┌────────▼────────────────────┐
 │  wifi/withwifi.c           │  │  file.c                     │
 │  (設定・接続・ディスパッチ)│  │  (FatFs APIラッパー)        │
 └──┬──────┬──────┬───────────┘  └────────┬────────────────────┘
    │      │      │                        │ f_open / f_read / f_write …
    │      │      │                        ▼
    │  DNS │  NTP │           ┌────────────────────────┐
    │      │      │           │  interface/ff.c        │
    │      │      │           │  FatFs R0.14b (ChaN)   │
    │      │      │           └────────────┬───────────┘
    │      │      │                        │ disk_read/write/ioctl
    │      │      │                        ▼
    │      │      │           ┌────────────────────────┐
    │      │      │           │  interface/pico-sdmm.c │
    │      │      │           │  SPI MMCドライバ        │
    │      │      │           └────────────────────────┘
    │      │      │
    ▼      ▼      ▼
┌──────┐ ┌────┐ ┌─────────────────────────────────┐
│socket│ │NTP │ │  TCP/TLS クライアント・サーバ    │
│.c    │ │clnt│ │  picow_tcp_client.c              │
└──┬───┘ └────┘ │  picow_tcp_server.c              │
   │            │  picow_tls_client.c              │
   ▼            └──────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│  CYW43 ドライバ（pico_cyw43_arch）               │
│  lwIP TCP/IP スタック                            │
│  mbedTLS                                        │
└─────────────────────────────────────────────────┘
```

---

## WiFiスタック

### withwifi.c — 設定・接続・BASICディスパッチ

`wifi/withwifi.c` はWiFiサブシステム全体のエントリポイントです。

#### INIファイルによる設定読み込み

起動時に `ini_file_wifi()` が呼ばれ、SDカード上の設定ファイルの各行をパースしてグローバル変数へ格納します。

| キー | 格納先 | 既定値 |
|------|--------|--------|
| `USEWIFI` | `g_usewifi=1` | 0（無効） |
| `WIFISSID=xxx` | `g_wifi_id[128]` | コンパイル時マクロ |
| `WIFIPASSWD=xxx` | `g_wifi_passwd[128]` | コンパイル時マクロ |
| `WIFICOUNTRY=XX` | `g_cyw43_country_char1/2` | `US` |
| `HOSTNAME=xxx` | `g_wifi_hostname[64]` | `PicoW` |
| `STATICIP=a.b.c.d` | `g_static_ip[4]` | なし（DHCP） |
| `NTPSERVER=xxx` | `g_ntp_server[64]` | `pool.ntp.org` |
| `INITIALNTP` | `g_initial_ntp=1` | 0 |

Sources: [wifi/withwifi.c:37-82]()

#### WiFi接続シーケンス

`connect_wifi()` は以下の手順で接続します。

```c
// wifi/withwifi.c:100-140 (簡略)
cyw43_arch_init_with_country(CYW43_COUNTRY(char1, char2, 0));
cyw43_arch_enable_sta_mode();
// WPA2-AES-PSK で最大5回リトライ (タイムアウト15秒/回)
cyw43_arch_wifi_connect_timeout_ms(ssid, passwd, CYW43_AUTH_WPA2_AES_PSK, 15000);
// 静的IPが設定されている場合はDHCPを停止して手動設定
if (g_static_ip[0]) dhcp_stop(&cyw43_state.netif[0]);
// INITIALNTP=1 なら NTP同期を最大5回試行
if (g_initial_ntp) lib_wifi(0, 0, LIB_WIFI_NTP);
```

Sources: [wifi/withwifi.c:97-156]()

#### `lib_wifi()` ディスパッチャ

BASICインタープリタから呼ばれるすべてのWiFi操作は `lib_wifi(r0, r1, r2)` に集約されます。`r2` がオペコード、`r0`/`r1` がオペランドです。

| `r2` (オペコード) | 操作 | 戻り値 |
|------------------|------|--------|
| `LIB_WIFI_IFCONFIG` | IPアドレス・サブネット・GW・DNS・MACアドレスを返す | `char*` |
| `LIB_WIFI_DNS` | DNS名前解決 (`dns_lookup()`) | `char*` (IP文字列) |
| `LIB_WIFI_NTP` | NTP時刻同期 | 0 (成功) |
| `LIB_WIFI_TCPCLIENT` | TCPクライアント接続開始 | エラーコード |
| `LIB_WIFI_TLSCLIENT` | TLSクライアント接続開始 | エラーコード |
| `LIB_WIFI_TCPSERVER` | TCPサーバ起動 | 0 |
| `LIB_WIFI_TCPSEND` | データ送信 | エラーコード |
| `LIB_WIFI_TCPRECEIVE` | バッファからデータ読み取り | 読み取りバイト数 |
| `LIB_WIFI_TCPSTATUS` | 接続状態照会 | 状態値 |
| `LIB_WIFI_TCPCLOSE` | 接続クローズ | エラーコード |
| `LIB_WIFI_TCPACCEPT` | 待機中の接続IDをFIFOから取得 | `void*` |
| `LIB_WIFI_ERR_INT` | 最後のエラーコード取得 | `int` |
| `LIB_WIFI_ERR_STR` | エラー文字列取得 | `char*` |

`g_wifi_enabled` が `0` の場合（接続前）、文字列返却系は空文字列、数値系はエラーコードを即座に返します。

Sources: [wifi/withwifi.c:200-285]()

#### DNS解決

`dns_lookup()` は lwIP の `dns_gethostbyname()` を呼び出し、`ERR_INPROGRESS` の場合はコールバックが来るまで10ms×最大150回（計1.5秒）ポーリングします。

Sources: [wifi/withwifi.c:90-115]()

---

### withoutwifi.c — 非WiFiビルド用スタブ

Raspberry Pi Pico（非WiFiモデル）向けビルドでは `wifi/withoutwifi.c` がリンクされます。すべてのWiFi関連関数がスタブとして定義されており、`connect_wifi()` は常に `1`（エラー）を返し、`lib_wifi()` は `r0` をそのまま返します。`board_led()` はCYW43ではなく汎用GPIO(`PICO_DEFAULT_LED_PIN`) で実装されています。

Sources: [wifi/withoutwifi.c:14-43]()

---

### socket.c — TCPバッファ管理層

`wifi/socket.c` はlwIPコールバックとBASIC APIの間に位置するバッファ管理層です。

#### 受信バッファ構造

受信データは動的に確保されたスロット型リンクリストで管理されます：

```text
g_socket_buffer (int*)
│
├─ buff[0] = 次バッファへのポインタ
├─ buff[1] = このスロットのデータ長（バイト）
├─ buff[2] = 現在の読み取り位置（バイト）
├─ buff[3] = 対応する tcp_pcb（識別子）
└─ buff[4..] = 実データ（可変長）
```

Sources: [wifi/socket.c:19-26]()

#### 主要関数

| 関数 | 役割 |
|------|------|
| `init_socket_system()` | 実行開始前の接続IDリセット |
| `init_tcp_socket()` | バッファ全解放・状態初期化 |
| `init_tls_socket()` | 上記 + TLSモードフラグ設定 |
| `tcp_receive_in_buff(data, bytes, pcb)` | lwIPコールバックからバッファへ追記 |
| `tcp_read_from_buffer(dest, bytes, conn_id)` | BASICから呼ばれるバッファ読み出し |
| `machikania_tcp_write(arg, len, conn_id)` | 最大 `WIFI_BUFF_SIZE`(2048B) 単位で送信 |
| `machikania_tcp_close(conn_id)` | クライアント／サーバ接続クローズ |
| `machikania_tcp_status(mode, conn_id)` | 接続状態・バッファ残量照会 |

`machikania_tcp_write()` はTLSモード時は `altcp_write()` / `altcp_output()`、プレーンTCPは `tcp_write()` / `tcp_output()` を使い分けます。送信後は最大100msポーリングして確認応答を待ちます。

Sources: [wifi/socket.c:175-220]()

#### サーバ接続管理FIFO

最大10個の接続IDを保持するFIFO (`g_pcb_fifo[10]`) があり、`shift_pcb_fifo()` / `add_pcb_to_fifo()` はARM割り込み無効化命令 (`cpsid i` / `cpsie i`) でアトミック操作を保証しています。

Sources: [wifi/socket.c:68-87]()

---

### TCP・TLSクライアント／サーバ

#### picow_tcp_client.c

`start_tcp_client(ipaddr, port)` がlwIPの `tcp_new_ip_type()` → `tcp_connect()` を呼び出し、接続完了コールバック `tcp_client_connected()` でヘッダバッファの自動送信と接続フラグの設定を行います。受信データは `tcp_client_recv()` コールバックから `tcp_receive_in_buff()` へ転送されます。

Sources: [wifi/picow_tcp_client.c:75-150]()

#### picow_tcp_server.c

`start_tcp_server(port, accept_mode)` がTCPリスナーを起動します。`tcp_accept_mode` の値によって、クライアント接続時 (`mode=0`) またはデータ受信時 (`mode=1`) にFIFOへ接続IDを追加するか決まります。

Sources: [wifi/picow_tcp_server.c:100-160]()

#### picow_tls_client.c

lwIPの `altcp_tls` レイヤを使用したTLSクライアント実装です。CA証明書検証は無効（`altcp_tls_create_config_client(NULL, 0)`）で、SNIは `mbedtls_ssl_set_hostname()` で設定します。mbedTLSの処理負荷を考慮し、TLS接続開始前にcore1を一時停止します（`stop_core1()` / `start_core1()`）。タイムアウトは15秒です。

Sources: [wifi/picow_tls_client.c:45-80, 145-165]()

---

### picow_ntp_client.c — NTP時刻同期

UDPを使ったSNTPクライアントです。

```mermaid
sequenceDiagram
    participant BASIC
    participant withwifi.c
    participant ntp_client.c
    participant lwIP/DNS
    participant NTPサーバ

    BASIC->>withwifi.c: LIB_WIFI_NTP
    withwifi.c->>ntp_client.c: get_ntp_time("pool.ntp.org")
    ntp_client.c->>lwIP/DNS: dns_gethostbyname()
    lwIP/DNS-->>ntp_client.c: ntp_dns_found() callback
    ntp_client.c->>NTPサーバ: UDP port 123 (NTP_MSG_LEN=48, req[0]=0x1b)
    NTPサーバ-->>ntp_client.c: ntp_recv() callback
    ntp_client.c->>ntp_client.c: seconds[40-43] - NTP_DELTA(2208988800)
    ntp_client.c-->>withwifi.c: time_t*
    withwifi.c->>withwifi.c: set_time_from_utc(now[0])
```

- 再送アラームは10秒後に `ntp_failed_handler()` を呼ぶ
- `dns_request_sent` フラグをポーリングしてブロッキング待機
- NTPエポック(1900-01-01)→UNIXエポック(1970-01-01)変換: `seconds_since_1900 - 2208988800`

Sources: [wifi/picow_ntp_client.c:90-135]()

---

### wifierror.c — エラー管理

エラー状態はグローバルな `g_err_wifi`（整数コード）と `g_err_str_wifi[32]`（文字列）の2種類で保持されます。

| 定数 | 値 | 文字列メッセージ |
|------|----|-----------------|
| `WIFI_ERROR_NO_ERROR` | 0 | `"No error"` |
| `WIFI_ERROR_CONNECTION_CLOSED` | 1 | `"Connection closed"` |
| `WIFI_ERROR_DNS_ERROR` | 2 | `"DNS error"` |
| `WIFI_ERROR_CONNECTION_ERROR` | 3 | `"Connection error"` |
| `WIFI_ERROR_WIFI_ERROR` | 4 | `"WiFi not connected"` |

なお `wifi.h` では `#define printf wifi_set_error(__LINE__); wifi_set_error_str` によりlwIP／mbedTLS内部のデバッグ出力をエラー管理に横取りしています。

Sources: [wifi/wifierror.c](), [wifi/wifi.h:1-10]()

---

## SDカードファイルシステム

### interface/pico-sdmm.c — SPI MMCドライバ

ChAN製のポータブルSPIドライバをRaspberry Pi Pico用にKenKenが改修したものです。ビットバンキングではなくPico SDK のハードウェアSPI (`spi_write_blocking` / `spi_write_read_blocking`) を使用します。

#### ピン設定

ボード構成ごとに `config/*.h` で定義されます。代表的な構成：

| ボード | CS | TX | RX | SCK | チャネル | ボーレート |
|--------|----|----|----|----|---------|-----------|
| pico_ntsc / pico_st7789 | 17 | 19 | 16 | 18 | spi0 | 10MHz |
| pico_restouch | 22 | 11 | 12 | 10 | spi1 | 16MHz |
| rp2350_lcd_1_47 | 15 | 11 | 12 | 10 | spi1 | 10MHz |
| xiao_ntsc | 6 | 3 | 4 | 2 | spi0 | 10MHz |

Sources: [config/pico_ntsc.h:113-118](), [config/pico_restouch.h:107-112]()

#### カード初期化シーケンス (`disk_initialize`)

```text
10ms 待機
↓
GPIO/SPI 初期化、80ダミークロック
↓
CMD0 (GO_IDLE_STATE) → Idleモード
↓
CMD8 (SEND_IF_COND, 0x1AA) → SDv2判定
  ├─ 成功: ACMD41(HCS=1) → CMD58 → CCS確認 → SDv2 or SDv2+Block
  └─ 失敗: ACMD41(0) ≦1 → SDv1, CMD1 → MMCv3
↓
SDv1/MMC: CMD16(SET_BLOCKLEN, 512)
```

Sources: [interface/pico-sdmm.c:195-260]()

#### ディスクI/O関数

| 関数 | SPI実装 |
|------|---------|
| `disk_read(drv, buff, sector, count)` | CMD17(単体) / CMD18+CMD12(複数) |
| `disk_write(drv, buff, sector, count)` | CMD24(単体) / ACMD23+CMD25+0xFDトークン(複数) |
| `disk_ioctl(CTRL_SYNC)` | select()でpending write確認 |
| `disk_ioctl(GET_SECTOR_COUNT)` | CMD9(CSD読み取り)で計算 |

Sources: [interface/pico-sdmm.c:265-370]()

---

### interface/ff.c — FatFsモジュール

ChAN製FatFs R0.14b そのものです。FAT12/FAT16/FAT32/exFAT をサポートし、`diskio.h` の `disk_read` / `disk_write` / `disk_ioctl` / `disk_initialize` / `disk_status` を通じて `pico-sdmm.c` と接続されます。

Sources: [interface/ff.c:1-80]()

---

### file.c — BASICファイル操作API

`file.c` はFatFs APIをBASICステートメントと関数へマッピングします。

#### ファイルシステム初期化

```c
// file.c:35-38
FATFS g_FatFs;
void init_file_system(void){
    if (FR_OK != f_mount(&g_FatFs, "", 0)) printstr("Initializing file system failed\n");
}
```

Sources: [file.c:35-38]()

#### BASICファイル操作一覧

`lib_file()` がすべての操作を `r2`（オペコード）で振り分けます。

| BASICステートメント/関数 | オペコード | FatFs API |
|------------------------|-----------|----------|
| `FOPEN(filename, mode, handle)` | `LIB_FOPEN` | `f_open()` (mode: "r"/"w"/"a"、"+"で読み書き兼用) |
| `FCLOSE [handle]` | `FILE_FCLOSE` | `f_close()` |
| `FILE handle` | `FILE_FILE` | ハンドル切り替え (1 or 2) |
| `FGET(bytes, addr)` | `FILE_FGET` | `f_read()` |
| `FPUT(bytes, addr)` | `FILE_FPUT` | `f_write()` |
| `FPUTC(char)` | `FILE_FPUTC` | `f_putc()` |
| `FGETC()` | `FILE_FGETC` | `f_read()` 1バイト |
| `FSEEK offset` | `FILE_FSEEK` | `f_lseek()` |
| `FSEEK()` | `FILE_FSEEKFUNC` | `f_tell()` |
| `FEOF()` | `FILE_FEOF` | `f_eof()` |
| `FLEN()` | `FILE_FLEN` | `f_size()` |
| `FINPUT([len])` | `FILE_FINPUT` | `f_gets()` 最大512バイト |
| `SETDIR path` | `FILE_SETDIR` | `f_chdir()` |
| `GETDIR$()` | `FILE_GETDIR` | `f_getcwd()` |
| `FFIND([pattern [, path]])` | `FILE_FFIND` | `f_findfirst()` / `f_findnext()` |
| `FINFO(n)` | `FILE_FINFO` | `fno.fsize` / `.fdate` / `.ftime` / `.fattrib` |
| `FINFO$(n)` | `FILE_FINFOSTR` | ISO-8601日時文字列生成など |
| `FREMOVE path` | `FILE_FREMOVE` | `f_unlink()` |
| `FRENAME old, new` | `FILE_FRENAME` | `f_rename()` |
| `MKDIR path` | `FILE_MKDIR` | `f_mkdir()` |

Sources: [file.c:120-290]()

#### ファイルハンドル管理

同時に2本のファイルを開けます。`g_pFileHandles[2]` と `g_FileHandles[2]` でポインタと実体を管理し、`g_active_handle`（1 または 2）がデフォルト対象を示します。`lib_fopen_main()` はオープン失敗時にまず `f_opendir(".")` でカード挿入確認を行い、未挿入なら `f_mount()` で再マウントを試みます。

Sources: [file.c:295-345]()

#### BASICファイルのコンパイル

`compile_file()` → `compile_file_sub()` はSDカード上の `.bas` ファイルをコンパイルします。クラスファイルは以下の優先順でサブディレクトリを探索します（RP2350/TypePU有無に応じた条件分岐あり）：

```text
1. /lib/<classname>/TYPEPU/PICO2/<classname>.bas  (PUERULUS && RP2350)
2. /lib/<classname>/PICO2/<classname>.bas          (RP2350)
3. /lib/<classname>/TYPEPU/<classname>.bas         (PUERULUS)
4. /lib/<classname>/<classname>.bas                (共通)
```

Sources: [file.c:55-150]()

---

## まとめ

Phyllosomaのネットワーク・ストレージ統合は、**compile-time切り替え**（`withwifi.c` vs `withoutwifi.c`）による移植性と、**統一ディスパッチャ**（`lib_wifi()` / `lib_file()`）によるBASIC APIの単純さを両立しています。TCPとTLSのコールバックはすべて `socket.c` のリンクリストバッファに集約されるため、BASICコードからは接続プロトコルを意識せずに `TCPRECEIVE` 一本で受信できます。SDカードドライバは完全なFatFs準拠で、ボードごとのSPIピン割り当てはコンフィグヘッダで吸収されています。

Sources: [wifi/wifi.h:1-60](), [wifi/withwifi.c:160-285](), [file.c:35-40]()
