soypat/cyw43439 の DHCP サンプル(main.go)を動かすための Wi-Fi 接続情報(SSIDとパスワード)は、指定されたディレクトリに ssid.text と password.txt という名前のテキストファイルを作成することで設定します。
TinyGoのビルド時に、これらのテキストファイルの中身が自動的に埋め込まれる仕組みになっています。
認証ファイルの作成場所と手順
examples/dhcp/main.go から見て、一つ上の階層にある cywnet/credentials/ ディレクトリの中にファイルを作成します。
cywnet/credentials/ ディレクトリ配下に、以下の2つのファイルを改行を入れずに作成
ssid.text
あなたのWi-FiのSSIDをそのまま入力して保存します。””はいれない。- password.text も同様
tinygo flash -target pico-w -monitor ./main.go でpicowがピカル
注:なるほど、ご自身のネットワークが 192.168.0.X なのに、コードが 192.168.1.99 を要求したまま正常に接続(DHCP完了)できてしまうのが不思議、ということですね!
これには、DHCPプロトコル(仕組み)の柔軟性と、今回のライブラリの処理が関係しています。不思議に思われるのも当然ですが、技術的には以下のような理由で「できてしまう」現象が起きます。
💡 なぜ動いてしまうのか? 2つの理由
理由1: ルーターが要求(192.168.1.99)を無視して「正しいIP」をくれたから
DHCPの仕組みにおいて、クライアント(Pico W)が送る requestedIP は、あくまで「このIPが使えたら嬉しいな」というただの希望(リクエスト)です。
ルーター(DHCPサーバー)はそれを受け取ったとき、以下のように判断します。
- 「うちのネットワークは
192.168.0.X だから、192.168.1.99 はダメだな」 - 「希望は却下するけれど、代わりにうちの空いている
192.168.0.X の正しいIPアドレスをあげるよ」
多くの一般的なルーターは、拒否(NAK)して通信を遮断するのではなく、このように親切に別の正しいIPアドレスを提案(Offer)してくれます。
理由2: ライブラリがルーターの提案を「そのまま受け入れた」から
このプログラムの後半(51行目付近)に、以下のコードがあります。
err = llstack.AssimilateDHCPResults(results)
AssimilateDHCPResults(DHCP結果の同化・適用)という関数が動いています。
これは、「最初に自分が何を指定していようが、最終的にルーターから『これを使ってね』と割り当てられた結果(results)をPico Wの通信スタックに正しく上書きして適用する」という処理をしています。
そのため、シリアルモニターのログ(ourIP)をよく見ると、最初に設定した 192.168.1.99 ではなく、自動的にルーターから付与された 192.168.0.X のアドレスに化けているはずです。
📊 ログで確認してみましょう
プログラムが成功したときに出力される DHCP complete のログを確認してみてください。
// おそらくこのようなログが出ているはずです
DHCP complete ... ourIP=192.168.0.X ... router=192.168.0.1
コードの requestedIP に何を書いていても、最終的にはルーターとPico Wの間で大人の解決(自動調整)が行われるため、不思議なことに何事もなかったかのように動いてしまうのです。 実際こうなっていた。賢いコードなり!
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
go version go1.24.2 linux/amd64
tinygo version 0.41.1 linux/amd64 (using go version go1.24.2 and LLVM version 20.1.1)
以下をmain.goとしてgo mod init main & go mod tidyとするとgo.modに管理情報が設定
そして必要なライブラリがgo getされる あとは以下でコンパイルしてモニターOK
build and monitor:: tinygo flash -target pico-w -monitor ./main.go
Connected to /dev/ttyACM0. Press Ctrl-C to exit.
--- Raspberry Pi Pico W Wi-Fi Connection Start ---
CYW43439 chip initialized successfully.
Connecting to Wi-Fi SSID: Pikara2-91d8e4 ...
Wi-Fi Connected successfully!
ーーーーーーーーーーmain.goーーーーーーーーーー
package main
import (
"fmt"
"time"
"github.com/soypat/cyw43439"
)
const (
ssid = "Pikara2-91d8e4"
password = ""
)
func main() {
// USBシリアル出力の安定待ち これ大事だろうと思う
time.Sleep(2 * time.Second)
fmt.Println("--- Raspberry Pi Pico W Wi-Fi Connection Start ---")
// 1. Pico W向けの専用関数でCYW43439デバイスを生成し、デフォルト設定で初期化
dev := cyw43439.NewPicoWDevice()
cfg := cyw43439.DefaultWifiConfig()
err := dev.Init(cfg)
if err != nil {
fmt.Printf("Failed to initialize CYW43439 chip: %v\n", err)
return
}
fmt.Println("CYW43439 chip initialized successfully.")
// 2. Wi-Fi ネットワークへ接続 (JoinメソッドとJoinOptionsを使用)
fmt.Printf("Connecting to Wi-Fi SSID: %s ...\n", ssid)
err = dev.Join(ssid, cyw43439.JoinOptions{
Passphrase: password,
})
if err != nil {
fmt.Printf("Wi-Fi Connection Failed: %v\n", err)
for {
time.Sleep(1 * time.Second)
}
}
// 3. 接続成功時の処理
fmt.Println("Wi-Fi Connected successfully!")
// プログラムが終了しないように維持
for {
time.Sleep(1 * time.Hour)
}
}
ーーmy http-client password.text ssid.textは同一フォルダにせんといかんかった
他のサンプルではcywnet。。。にいれて成功したが。。。理由不明、動けばいいーーーーーーー
package main
// 警告: TinyGoでの動作にはコンパイル時に -scheduler=tasks の指定が必須です
import (
"bufio"
"fmt"
"log/slog"
"machine"
"net/netip"
"time"
"github.com/soypat/cyw43439"
"github.com/soypat/cyw43439/examples/cywnet"
"github.com/soypat/cyw43439/examples/cywnet/credentials"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/tcp"
)
const tcpBufSize = 2030
func main() {
// シリアルモニタ接続のための待機
time.Sleep(2 * time.Second)
fmt.Println("\n--- Pico W HTTP Client Start ---")
// 1. ログハンドラの設定
logger := slog.New(slog.NewTextHandler(machine.Serial, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
// 2. Wi-Fi設定とネットワークスタックの初期化
devcfg := cyw43439.DefaultWifiConfig()
devcfg.Logger = logger
cystack, err := cywnet.NewConfiguredPicoWithStack(
credentials.SSID(),
credentials.Password(),
devcfg,
cywnet.StackConfig{
Hostname: "tinygo-http",
MaxActiveTCPPorts: 1,
},
)
if err != nil {
panic("Network init failed: " + err.Error())
}
// 3. バックグラウンドでネットワーク処理
go func() {
for {
send, recv, _ := cystack.RecvAndSend()
if send == 0 && recv == 0 {
time.Sleep(5 * time.Millisecond)
}
}
}()
// 4. DHCPでIPアドレスを取得
fmt.Println("Acquiring IP address via DHCP...")
dhcpResults, err := cystack.SetupWithDHCP(cywnet.DHCPConfig{})
if err != nil {
panic("DHCP failed: " + err.Error())
}
logger.Info(
"DHCP complete",
slog.String("addr", ipv4.String(dhcpResults.AssignedAddr4)),
)
// 5. 接続先サーバー
serverAddrStr := "192.168.0.5:8080"
svAddr, err := netip.ParseAddrPort(serverAddrStr)
if err != nil {
panic("parsing server address: " + err.Error())
}
stack := cystack.LnetoStack()
rstack := stack.StackRetrying(cywnet.DefaultStackBackoff)
// 6. TCPコネクションの準備
var conn tcp.Conn
err = conn.Configure(tcp.ConnConfig{
RxBuf: make([]byte, tcpBufSize),
TxBuf: make([]byte, tcpBufSize),
TxPacketQueueSize: 3,
Logger: logger,
})
if err != nil {
panic("conn configure: " + err.Error())
}
// 7. TCP接続
lport := uint16(stack.Prand32()>>17) + 1024
fmt.Printf("Dialing %s...\n", serverAddrStr)
err = rstack.DoDialTCP(&conn, lport, svAddr, 5*time.Second, 3)
if err != nil {
panic("TCP dial failed: " + err.Error())
}
defer conn.Close()
// 8. HTTP GETリクエストを送信
fmt.Println("Sending HTTP GET request...")
fmt.Fprint(&conn, "GET /update?t=41&h=67 HTTP/1.1\r\n")
fmt.Fprint(&conn, "Host: 192.168.0.5:8080\r\n")
fmt.Fprint(&conn, "Connection: close\r\n")
fmt.Fprint(&conn, "\r\n")
// 9. HTTPレスポンスを表示
fmt.Println("--- HTTP Response ---")
scanner := bufio.NewScanner(&conn)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Printf("Error reading response: %s\n", err.Error())
}
fmt.Println("---------------------")
}