2020年5月17日日曜日

Wio Terminalを使ってGPSなし・Wi-Fiだけで測位してみる(Google Geolocation API使用)


なんだかSeeed StudioのWio Terminalが話題ですね。
秋月やスイッチサイエンスでも取り扱いが始まりましたが、
即在庫切れになったりしているようで大人気みたいです。



会社で買ってもらったので、週末いろいろいじってみました。
内容としてはMicroChipのATSAMD51系MCUに、RealtekのRTL8720DNが乗っていて、
M5Stackのように画面やら何やらいろいろついてケースに入っている豪華マイコンボードです。
公式通販
https://jp.seeedstudio.com/Wio-Terminal-p-4509.html
公式Wiki
https://wiki.seeedstudio.com/jp/Wio-Terminal-Getting-Started/

RealtekのRTL8720DNは、先に出ているESP8266/ESP32とは違い、
5GHzのWi-Fiに対応した新しいWi-Fi搭載マイコンです。
これを早く使ってみたいという人も多かったはずで、
Wio Terminalの登場により、いち早く技適マーク付きの試せる環境が手に入りました。
どうやらメインマイコンのSAMDと、Wi-Fi用のRTL8720DNはSPIでつながっており、
このライブラリでArduino Wi-Fi API互換にラップしていますが、
実は内部ではATコマンドを叩いて通信しているようです。すごい!



さて、本題ですが、せっかくなので温めていたネタを、
この新しいボードで試してみようとやってみたのが今回のWi-Fiを使った測位です。

スマートフォンなんかではよくWi-Fiをオンにするとマップアプリの測位が早くなったり、
正確な位置が表示されやすくなったりするのはご存知な方も多いと思います。
これは、Googleのデータベースから、
飛んでいるWi-Fi APのMACアドレスと緯度経度を紐付けた情報が提供されているから
実現できています。
(Google以外にも、Apple他も同じことをしているのかもしれませんが、
よく知りません。)

あまり知られていませんが、Wi-Fi APの情報から現在位置を推定するAPIは公開されていて、スマートフォンに組み込みの機能以外からもアクセスできるようになっています。
今回はこれを使ってみようと思います。

先に準備を。
Google アカウントにログインして、APIコンソールを開きます。
https://console.developers.google.com/?hl=JA


 すでにプロジェクトがある場合もあると思いますが、
ここでははじめに新規プロジェクトを作っていきます。
Google APIsと書いてある文字の右側をクリックしてプロジェクトの選択画面を表示


新しいプロジェクトボタンをクリック


適当にわかりやすい名前をつけます。


こんな画面に飛ばされたら、APIとサービスを検索の検索窓に
Geolocationと入力




Geolocation APIが出てきたらクリック


有効にする。


左側にある認証情報をクリック


今回は使わないですが、OAuthの同意画面を作成


外部を選択。次の画面の写真はありませんが、
名前の場所だけ埋めればとりあえず使えます。
このへんからはセキュリティに関わってくるので、各自調べながらやってください。


OAuth認証画面が作成できたら、
認証情報のページで認証情報を作成→APIキーをクリック


APIキーが取得できたら準備完了です。

このレポジトリにWio Terminalに書き込むスケッチと、Node.jsスクリプトがあります。

git cloneしてArduino IDEで書き込んだら、SDカードを入れて起動するだけで、
30秒おきにスキャンした周囲のWi-Fi APの情報がSDカードに保存されていきます。

終わったら、PCにSDカード内のbssids.csvをコピーします。
↑のレポジトリに入っている、getloc.jsと同じディレクトリにコピーしてください。

Node.jsの実行環境がない場合は、適当にインストールしてください。
また、CSVパーサーライブラリのインストールが必要です。
適宜
npm install csv-parse
とかやってインストールしましょう。

get_loc.jsを開いて、YOUR_API_KEYの部分に先ほど取得したAPIキーを貼り付けて、
node getloc.js
すると、result.csvというファイル名で緯度経度に変換された結果が保存されます。

読み方ですが、1列目の数字はタイムスタンプ(起動時からのミリ秒)、
2列目に緯度、経度、誤差(m)と続く構造です。

めんどくさがって結果出力をKMLファイルとかにしてないわけですが、
世の中には便利なサイトがありまして、CSVを貼るとKMLにしてくれるサイトがあります。

ここに、CSVの中身を貼って、


こんな設定にしてあげるとあらかんたん、KMLファイルとして出力されます。
(ピンのタイトルがタイムスタンプになります)

Google Mapsのマイマップ機能やGoogle Earthにインポートすると結果が見られます。

こんな感じでわりとかんたんにWi-Fiだけを使った測位ができてしまいます。

Wio Terminalには3G/LTEのようなWWANがありませんが、
テザリングやモバイルルータを使うなどすれば
リアルタイムでの測位も可能と思います。
(その場合はモバイルルータのMACアドレスを除外するなど
ちょっと工夫が必要そうです。)





なお、Google Geolocation APIがなぜそれほど浸透していないのか、
理由はおそらく簡単で、料金だと思います。
↑の画像にも実はチラっと写っていますが、
利用料金なんと1000リクエストで5USDくらいだそうです。
この記事を読んで試す場合、さすがに無料枠内で試せると思いますが、
高頻度で24時間動かし続けたり、商用で何台もクライアントがいると
かなりの金額になりそうです…。

2020年3月3日火曜日

怪我をしたので松葉杖に取り付ける荷重管理デバイスを製作しました。

数年前に下書きを作って公開してない記事でした。令和になって初公開です。

膝の内側を怪我しました。前十字靭帯付着部裂離骨折という骨折のようです。
経緯についてはこちらの動画をどうぞ…

前十字靭帯の付け根にあたる膝の骨を剥離骨折する、
"前十字靭帯付着部裂離骨折"という怪我のようです。
骨にトンネルを開け、ワイヤを通し靭帯と結び、浮いた骨を元の位置に抑える手術をしました。
入院は一週間半、私は大学生で学期末の時期だったので
ちょっと成績的にまずくなったりしましたが、大学生活に疲れ精神的にも参っていた中、
一時の休みを得られてよかったとも感じています。
名医に出会えたので、術後の経過は良好で疼痛もなく順調でしたが、
骨が接合するまでには3ヶ月の時間を要するということで、術後2ヶ月は杖を欠かせませんでした。

術後の経過日数により、はじめの2週間は負傷側の足を地面につくことなく過ごすこと、
次の週は体重の1/3まで掛けてよい…など、徐々に負荷制限が緩和されていきました。
しかし、極度の運動音痴の私は、1/3荷重がこれくらいだと両足別々の体重計に乗せられて、
教えられたところで感覚を覚えられません。負傷側にも体重をかければ、
松葉杖を持つ手は楽なのですが、荷重制限を越えるとどうなるかわかったものではありません。
一方で、荷重を掛けず養生している負傷側はどんどん衰えていき、リハビリの先生には、
「寝たきりの老人並」と言われました。太ももが頬のように柔らかくなったのはショックでした。
荷重を掛けなければ筋肉を取り戻すことはできないのです。

そんなジレンマを抱えつつ、やはりこわいので余り荷重を掛けないようにしていたまま、
全体重の2/3荷重を負傷側に掛けていいと許可されました。
2/3荷重を掛けられると、松葉杖を両手に抱えずに1本で歩く、「片松葉」ができるようです。
以前と同じ荷重のままでは、手に掛かる荷重は両松葉のときの2倍になってしまいます。
右手がボロボロになりながら、ちゃんと2/3荷重を掛けられるようになろう。と考えました。

前置きが長くなりましたが、製作工程を…。

Wii fitというゲーム用コントローラーのバランスWiiボードをAmazonで買いました。
早速バラし…


松葉杖用の先端に取り付けました

テープ固定じゃ心もとないので、Amazonで杖の先のゴム石突部を買って加工しねじ止めしました。


測定値を正確にするために、校正用の機能と設備も用意。


で、ちょいちょいっとしきい値を設定してLEDが点くようにしてみました。

ESP32でBosch BME680の拡張ライブラリ Bsecを使う

少し前に、秋月でも気温・湿度・気圧に加え、MEMSガスセンサを搭載した、
Bosch BME680の取り扱いが始まりましたね。
サクっと4ピン出ていてすぐにI2Cで繋げられそうな感じになっています。
自分は去年しばらくストロベリーリナックスのBME680を使って
しばらく実験していたので、使い方について少しまとめてみます。

BME680は、公開されているデータシートやサンプルコードを使うと
・気温
・湿度
・気圧
・ガスセンサの抵抗値
が取得できるセンサーです。
ガスセンサの構造ですが、電気応用みたいな名前の科目で習った通りだとすると
金属をヒーターで温める→酸化して抵抗値が上がる
周辺の雰囲気に水素などがあると還元されて抵抗値が下がる
という原理で可燃性ガスの有無がわかるという原理だったと思います。
なのでセンサがしっかりと加熱されるまでしばらく新鮮な雰囲気下で動かして、
ベースラインの抵抗値を控えておいて、それより抵抗値が下回ったら
何かガスが出てるなという判断が可能です。

しかし、抵抗値が表示されても相対的な評価しかできないので、
何か単位がついた空気品質の指標が欲しいですよね。

そこでBosch謹製のBsecライブラリというものをご紹介します。
ボッシュのラボテストの結果、センサーから取得できる生データをもとに、
・推定CO2
・IAQ(屋内空気品質)
・推定呼気VOC
・湿度・気温補正値(ヒーターの影響を考慮した値)
を計算してくれるArduinoライブラリです。(RPi版もある)

最新版はGitHubにあります。早速cloneしましょう。

README.mdを読むとわかるのですが、インストールに一癖も二癖もあります。
なぜかというと、BoschはこのBsecライブラリのソースコードを公開していません。
特定環境向けに、コンパイル済みバイナリとヘッダーファイルという形で提供しています。
このコンパイル済みのライブラリを使うために、Arduino IDEに修正が必要です。

現時点で最新のインストール方法についてご説明します。

はじめに、Arduino IDE 1.8.11のポータブル版を用意します。
ここに過去バージョンの一覧があるので入手可能です。
Windows ZIP file for non admin installとあるリンクからZipファイルになったポータブル版をダウンロードして解凍しました。Macの場合は知りません。

解凍したら、できたフォルダにportableという名前のフォルダを作成します。
これですでに入っているArduino IDEとパスを共有しなくなります。
ライブラリやボードマネージャの設定などは入れなおしになります。

フォルダ内のexeからArduino IDEを起動します。
今回はESP32を使うので、ボードマネージャでESP32を使えるように設定し、(省略)
ライブラリをインクルードより、まずは普通にダウンロードした
Bsecライブラリをインポートします。
なお、ESP32ボードのパッケージはv1.0.4を使います。


Arduino 1.8.11のフォルダ\portable\packages\esp32\hardware\esp32\1.0.4
を開きます。



platform.txtを開き、
compiler.c.extra_flags=
compiler.c.elf.extra_flags=
compiler.S.extra_flags=
compiler.cpp.extra_flags=
compiler.ar.extra_flags=
compiler.objcopy.eep.extra_flags=
compiler.elf2hex.extra_flags=

あたりを

compiler.c.extra_flags=
compiler.c.elf.extra_flags=
#compiler.c.elf.extra_flags=-v
compiler.cpp.extra_flags=
compiler.S.extra_flags=
compiler.ar.extra_flags=
compiler.elf2hex.extra_flags=
compiler.libraries.ldflags=
とします。(コメントアウトが増えてるけど公式のやつそのままコピペなので)



さらに、
## Combine gc-sections, archives, and objects
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {build.exception_flags} -Wl,-Map "-Wl,{build.path}/{build.project_name}.map" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{archive_file_path}" -Wl,--end-group  "-L{build.path}"

の行を

recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -Wl,--start-group {object_files} "{archive_file_path}" {compiler.c.elf.libs} -Wl,--end-group {compiler.libraries.ldflags} -Wl,-EL -o "{build.path}/{build.project_name}.elf" -lm -lgcc

とします。
これでコンパイルが通るようになります。



そして、Arduino IDEを再起動後、スケッチ例→Bsec Software Library→basic
をコンパイルしてみましょう。I2C接続でセンサーデータを取得するサンプルです。
必要に応じて、LED_BUILTINを#define LED_BUILTIN 19のように置き換え
BME680_I2C_ADDR_PRIMARYを0x77(BME680_I2C_ADDR_PRIMARYは0x76)
としてあげると動くと思います。
ESP32の場合、Wire.beginの部分で
Wire.begin(21, 22);のようにピンを置き換えしてあげる必要があります。


成功するとこんな感じでセンサーデータが出てきます。
呼気VOCなんかは簡易アルコールチェッカーとして使えそうだったり、
気温測定の代わりに今流行っているCO2も推定値が出てきます。
また、空気品質もIAQという指標となる値が出てくるため評価しやすい値となっています。

春休みにBME680をいじってみてはいかがでしょうか。

2020年2月16日日曜日

ESP-IDF v4をWindowsのVS Codeで使いやすくしてみる(PlatformIOなし)

https://blog.nyancotech.online/2020/02/windowsesp32esp-idf.html
の続きです。
WindowsでESP-IDF v4環境の構築は上記ページをご覧ください。

まずは普通にインストールされたESP-IDF Command Promptを開きます。



Using Python in C:\Users\admin\AppData\Local\Programs\Python\Python37\
Python 3.7.3
Using Git in C:\Program Files\Git\cmd\
git version 2.25.0.windows.1
Setting IDF_PATH: C:\Users\admin\esp\esp-idf
この部分をメモっておきます。


VS CodeでESP-IDFディレクトリを、「フォルダを開く」から開いたあと、
左下の歯車ボタンから設定を開きます。


Workspaceタブを選択し、Terminal欄をクリック、Edit in settings.jsonをクリック


{
    "terminal.integrated.shellArgs.windows": [
        "cmd",
        "/k",
        "..\\.espressif\\idf_cmd_init.bat",
        "C:\\Users\\admin\\AppData\\Local\\Programs\\Python\\Python37\\",
        "C:\\Program Files\\Git\\cmd\\"
    ]
}
ここに上のJSONを環境に合わせて変更して入力後保存します。
..\\.espressif\\idf_cmd_init.bat

の行には、インストールした.espressifディレクトリ内の
idf_cmd_init.batへのパスを入力してください。
自分は

esp- .espressif
     L esp-idf
というディレクトリ構成にしているので
カレントディレクトリのesp-idfディレクトリから見ると
..\.espressifが.espressifの場所になってます。
Python37というのが書いてある行は、
初めにESP-IDF Command Promptで表示されていた
「Using Python in...」の右に書いてあるディレクトリを入力します。
Git\\cmdというのが書いてある行は、同じく
「Using Git in...」に書かれている行を入力します。

それぞれパスの区切り文字はエスケープ文字として認識されてしまうので、
\\と2つ続けて入力してください。




settings.jsonを保存して、Terminal->New Terminalでターミナルを開くと、
VS CodeからESP-IDF Command Promptが使用できます!
idf.py flash -p COM10とかやればこの画面のまま書き込みまでできます!


2020年2月15日土曜日

【令和最新版】WindowsにESP32開発環境ESP-IDFをインストールする

これまでESP32の開発環境はUnixシェル用のものしか用意されておらず、 Windowsではmsysを使って実行する方法が公式で紹介されていました。 そんな中、最近になって、Espressif純正の一括インストーラが出ていたので試してみました。
https://docs.espressif.com/projects/esp-idf/en/latest/get-started/windows-setup.html

ダウンロードしたexeファイルを開き、インストールしていきます。


必要なパッケージをインストールしてくれますが、Pythonやgitなど、
すでに入っているものがあればそれを選択することで
新しくインストールすることなく進むことができます。
よくできてますね


ESP-IDFのレポジトリをすでにダウンロードしてあれば
そのディレクトリを使うことができます。



ESP-IDFをダウンロードする場合はバージョンを選択できます。
使用するコンポーネントの互換性の問題などがなければ、
4.0を選んだほうが後々アップデートする手間を減らせるかもしれません。


ツールチェーンやPythonの仮想環境を保存する先を選択します。


一発でESP-IDF用のターミナルを開くショートカットを
スタートメニューやデスクトップに登録できます。


各種設定が完了するといよいよインストールです。


ESP-IDFレポジトリはとても大きいのでダウンロードに時間がかかります。



しばらくして、インストールができ…


ませんでした。

表示されたログを見ると、Python virtualenvに
--no-site-packeagesなんてオプションないよと言われています。


はてさてexeファイルの中身に入ってるんだろうしどうしたものかと調べていると、
修正版が出ていました。
下のほうに直リン貼ってる人がいます。




気を取り直してやり直し。


インストールできました。


Pythonのパッケージは専用の環境としてインストールされ、
PATHもシステム標準のものではなく専用のPATHが設定されるようです。
ほかの開発環境と競合せずに動いてありがたいですね。
早速esp-idf\examples\get-started\hello-worldディレクトリで
make menuconfig相当の"idf.py menuconfig"をしてみると…
なんかUnicodeDecodeErrorとか言われています…。


ちょっとぐぐったらこんなすばらしいコピペで動きそうな記事を発見

最近のUnicodeDecodeError

.espressif\python_env\idf4.0_py3.7_env\Lib\site-packages\sitecustomize.py
に上記記事のように
import builtins
__original = open
def __open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None):
    if 'b' not in mode and not encoding:
        encoding = 'utf-8'
    return __original(file, mode, buffering, encoding, errors, newline, closefd, opener)
builtins.open = __open

と記述したファイルを置き、標準のopen関数を上書きしてあげます。


すると…

うーんなんか周り文字化けてるけどとりあえず動いた!



このように、Espressif Systems様が提供してくださっている
Windows版インストーラにより、

簡単にWindowsでの開発環境が作れました!

(正直これでも今までよりは楽だと思います)

あとはメモですが
idf.py menuconfig
でmenuconfig
Serial flusher configでフラッシュの設定ができたりします。
idf.py makeでビルド
idf.py flash -p COMxxでCOMxxポートに接続されたESP32へ書き込み
idf.py monitor -p COMxxでシリアルモニタ(トレースバック付き)
monitor中に
Ctrl+T -> Ctrl+Shift+Fで再書き込み

2019年11月16日土曜日

ROHM BP35A1 ローム製Wi-SUN通信モジュールを使ってスマートメーターにBルート接続する

TwitterでM5StickC用にRohmのBP35A1を取り付けるアダプターを
作っておられる方がいて、BOOTHで購入できたので買ってみました。


サンプルコードも配布されていますが、開いてみるとMicroPythonだったので、Arduinoのスケッチを書いてArduinoで使ってみました


そもそもBルートとはなにか?
電力会社の設置している、各家庭にある電力量計は、アラゴの円板を使っていたアナログのものからデジタル化されています。
デジタル化されたメーターは、サブギガ(920MHzあたり)を使う無線で通信ができるようになっています。

なんのために無線通信できるのか?
毎月の検針を、検針員がメーターを直接読み取らなくても手元のプリンターで積算電力量を受信して印刷できます。
植栽や建物の密集度の関係上検針しにくい住宅や、集合住宅などでは集合ポスト前で一括受信・印刷することができるのだと思います。検針員はとても省力化できると思います。実際のところ、電波の到達距離も大したことがないため、どのような運用をしているのかはわかりません。
また、遠隔で電力供給の休止を行えるとされています。

これらはすべて電力会社が電力の使用を監視・制御するための通信で、Aルートと呼ばれています。
今回の話題であるBルートは、需要家(ユーザー)ごとにスマートハウスのようなHEMS機器とスマートメーターを直接無線接続して、使用電力のマネジメントなどを行うための仕組みです。

現在の使用電力量や各電力機器の動作状況をHEMS機器同士で共有することで、効率的に電力の使用ができるとされています。

今回使ったモジュールはこのようなHEMS対応機器を開発するための通信モジュールです。

スマートメーターと通信モジュールをBルート接続して使うためには電力会社にBルート接続用ID、パスワードを発行してもらう必要があります。
東京電力の場合、スマートメーター化されていない電力量計であっても、ID発行の申請を行うことでスマートメーターへの切り替えも行えるようです。(要工事)

BP35A1はUARTでシリアル通信コマンドを使って制御します。
公式のコマンドリファレンスは日本語ですが、この手のモジュールを使ったことがないと、読んでも心が折れそうだと思います。

ざっくりとした流れとしては

Bルート接続用ID/パスワードを設定
スマートメーターの電波をスキャン
スキャン結果からスマートメーターの接続情報を調べる
スキャン結果の接続情報を接続先として設定
接続開始・完了
電力取得コマンド送信
結果受信

と、初期化シーケンスがいろいろあってめんどくさいです。
とりあえずシリアルターミナルで手打ちして接続までいけるようにこれら一連の流れをメモしておきます。
上記のWi-SUN HATの場合、ピンを下にして裏面を向けて、
左側のピンから
GND/NC/NC/TX/RX/NC/3.3V/NC
というピン配置になっているため、電源とUARTを接続すればUSBシリアル変換などを使って使うこともできます。
115200bps 3.3VのUART 改行コード\r\nです。

以下コマンドでスマートメーターの接続までを行います。

Bルートのパスワードを設定
SKSETPWD C passwordhere

BルートのIDを設定
SKSETBID idhere

アクティブスキャンを実行
SKSCAN 2 FFFFFFFF 6

結果が返ってくる
EVENT 20 FE80:0000:0000:0000:xxxx:xxxx:xxxx:xxxx
EPANDESC
   Channel:31
   Channel Page:09
   Pan ID:ABCD
   Addr:xxxxxxxxxxxxxx
   LQI:93
   PairID:00E79995
 EVENT 22 FE80:0000:0000:0000:xxxx:xxxx:xxxx:xxxx


取得したスマートメーターのアドレスをIPv6アドレスに変換
 SKLL64 xxxxxxxxxxxxxx  //Addrの値
 FE80:0000:0000:0000:xxxx:xxxx:xxxx:xxxx

チャンネルを設定
SKSREG S2 31  //Channelの値

Pan IDを設定
SKSREG S3 ABCD  //Pan IDの値

接続開始
SKJOIN FE80:0000:0000:0000:xxxx:xxxx:xxxx:xxxx  //IPv6アドレスに変換後の値

EVENT 25が来ると接続成功です。
私は当初、EVENT 24で接続失敗しかしなかったのですが、
東電のフォームでパスワードを変更依頼したところ、以降つながるようになりました。
以前のパスワードではスマートメーターの設定がうまくできていなかったのかもしれません。

接続完了後、電力取得のコマンドを送信します。

SKSENDTO 1  FE80:0000:0000:0000:xxxx:xxxx:xxxx:xxxx 0E1A 1 000E
000Eのあとに半角スペースを入れて、続いてバイナリで
{0x10, 0x81, 0x00, 0x01, 0x05, 0xFF, 0x01, 0x02, 0x88, 0x01, 0x62, 0x01, 0xE7, 0x00} + \r\n
を送信すると瞬時電力が取得できます。

成功すると
ERXUDP FE80:0000:0000:0000:xxxx:xxxx:xxxx:xxxx
FE80:0000:0000:0000:xxxx:xxxx:xxxx:xxxx 0E1A 0E1A xxxxxxxxxxxxxxx 1 0012 xxxxxxxxxxxxxxxxxxxxxxxxxxxx000000BA
のようなレスポンスが返ってきます。
ここで見るのは最後の8桁、000000BAです。
これをそのまま10進に変換して、0xBA=瞬時電力186W
というような感じで読むようです。
上記サンプルコードでは、M5StickCで本体の液晶画面にこの数字を取得表示するようにしています。
Arduinoのスケッチなので、これを改良して、Wi-Fi経由でデータ送信して、
遠隔地から電力量を確認するような使い方も可能です。

それほど死ぬほど難しいわけではないですが、まだまだ情報量が少なく、
活用例も乏しいためもっと使う人が増えると楽しいのかなと思います。


2019年4月28日日曜日

ESP32をWi-Fi AP/イーサネットコンバーターにする(LAN8720使用)

最近になって、昨年公式githubに”eth2wifi”というサンプルが上がっているのに気づきました。
どうやらこれを使うと、有線LANをつないでWi-Fiアクセスポイントを立ち上げる
あるいはESP32がWi-Fiアクセスポイントに接続して、ESP32を介して有線LANの機器をLANに接続する
ということができるようです。1000円そこらでAPが作れるのは画期的です。

ESP32は、Wi-Fiの他に、物理層の部分以外Ethernetにも対応しています。
実際にEthernetを使うためにはEthernet PHY(フィジカル=物理層)
の回路が必要です。
よく知りませんが、通信用のクロックを作ったり、
信号レベルなどを有線LANの規格にあわせたりしてくれる回路なのだと思います。
これらの回路をESP32内部に入れるのはあまり使う人いないのに不毛ということで
物理層がはじめからは載っていないのだと思います。僕もそれでいいと思います。

調べるとEthernet PHYのICというものがいろいろ出ていて、簡単にブレークアウトボードに乗っているものが手に入るICとして、LAN8720がESP32ではよく使われているようです。

というわけで、LAN8720の載った基板をAliexpressで注文しました。
200円くらいで売っています。
https://www.aliexpress.com/item/1pcs-Smart-Electronics-LAN8720-module-network-module-Ethernet-transceiver-RMII-interface-development-board-for-arduino/32947407343.html?spm=2114.search0204.3.41.18a547acrOaaW9&ws_ab_test=searchweb0_0,searchweb201602_4_10065_10068_319_10059_10884_317_10887_10696_321_322_10084_453_10083_454_10103_10618_10307_537_536,searchweb201603_16,ppcSwitch_0&algo_expid=a53a62b5-87f4-46c8-8fea-e77569fc6b6b-6&algo_pvid=a53a62b5-87f4-46c8-8fea-e77569fc6b6b&transAbTest=ae803_5

届いたので、
冒頭のリンクのURLに書いてあるとおり配線しました。


 まあたくさん配線しないといけなくて、めんどくさいですがジャンパ線で直接接続のバラックです。
リンク先に注意書きが書いてあるのですが、
LAN8720ボードからのクロック出力をGPIO0に入力していますが、
ESP32の起動時にGPIO0がローに落ちていると、ファーム書き込みモードに入ってしまいます。
そこで、調べたら出てきた対策をします。
発振子の足をNCのピンに引き出して、GPIO17につなぎます。
GPIO17はコードの中でクロック出力の有効化・無効化をしてくれています。
こんなやり方でいいのか?という感じがしますがこれでうまくいきました。
基板を立ち上げるなら、トランジスタで電源ごと切ったりするほうが健全な気もします。



eth2wifiサンプルのディレクトリで、make menuconfigで動作の設定ができます。


僕が買ったボードのPHY Addressは1でした。
USE PHY Power pinにチェックが入っていると、PHY Power GPIOで制御ができます。
WiFi station modeにチェックが入っていると、イーサネットコンバーター
チェックを外すとAPになります。

実際通信してみましたが、CATV12Mではほぼ上限の値が出ていたので、実際にはもっと早いスピードで通信できると思います。
ツイッターを見ながらプライムビデオを流すくらいの使い方は余裕でできました。
本当に1000円そこらでAPを自作できるのはすごいですね。
2つ数珠つなぎにしてAP-イーサネットコンバーターとやれば中継機とかも組めそうです。
(それはもっとうまいやり方がありそうですが)

どうやってこれを実現しているのか?コードを見るととてもシンプルで驚きます。

static esp_err_t tcpip_adapter_sta_input_eth_output(void* buffer, uint16_t len, void* eb)
{
    if (ethernet_is_connected) {
        esp_eth_tx(buffer, len);
    }

    esp_wifi_internal_free_rx_buffer(eb);
    return ESP_OK;
}

Wi-Fiに受信バッファがあったらEthernetに送る
Ethernetに受信バッファがあったらWi-Fiに送るというような関数があります
シリアル通信のようなノリでこんなことができているのですね。
パケットをそのまま処理しているので、ESP32自身に宛てられたパケットを判別して処理するようなコードを書かないとESP32自身は通信ができないような気がします。
市販のAPのようにWebUIを実装したりするのは結構たいへんそうです。
ボタンを押しながら起動で別モードとして設定モードが立ち上がるような実装だと特に問題なく作れそうです。