ESP-IDFを使ってみる

MESH WiFi


こちらにESP- Mesh-Liteのサンプルが公開されています。
詳細な情報はこ ちらにメチャクチャ詳しい資料が有りますが、要するにWiFiを使ったMesh-Networkを構築することができます。
以前はESP-MDFのESP-WIFI-MESHとして公開されていた技術です。
名前が紛らわしいですが、ESP-Mesh-LiteとESP-WIFI-MESHIのAPIは完全に別物になっています。

ESP-MDFではESP-WIFI-MESHだけでなく、以下の機能を利用することができます。
Mwifi : ESP-WIFI-MESHに再送信フィルタ、データ圧縮、断片化送信、P2Pマルチキャスト機能を追加します。
Mespnow : ESP-NOWに再送信フィルタ、巡回冗長検査(CRC)、およびデータ断片化機能を追加します。

ESP-MDFはこちらで公開されて いますが、2021年からメンテナンスされていません。
ESP-MDFは、ESP-IDF V4.3.1を対象としていて、ESP-IDF V5に未対応です。
当然、ESP-MDFはESP32Cシリーズにも未対応ですが、ESP-Mesh-LiteはESP32Cシリーズにも対応しています。

そこで、こ ちらのサンプルを使ってesp-mesh-liteの機能を簡単に紹介します。
この環境を確認するためには、最低でも2台のESP32が必要になります。
インストールは単純にリポジトリをクローンするだけです。
$ git clone https://github.com/espressif/esp-mesh-lite
$ cd esp-mesh-lite/examples/no_router
$ idf.py menuconfig

1台目のESP32をRoot Deviceとしてビルドします。
2台目のESP32を非Root Deviceとしてビルドします。
RootのESP32には以下の様に表示されます。
これは「3c:71:bf:9d:bd:00」のノードが、Child NodeとしてMesh-Networkに組み込まれたことを示しています。
I (1981200) no_router: System information, channel: 11, layer: 1, self mac: 24:0a:c4:c5:46:fc, parent bssid: 00:00:00:00:00:00, parent rssi: -120, free heap: 204024
I (1981205) no_router: Child mac: 3c:71:bf:9d:bd:00
MeshLite nodes 2:
1: 2, 3c:71:bf:9d:bd:00, 192.168.5.2
2: 1, 24:0a:c4:c5:46:fc, 0.0.0.0

2台目のESP32には以下の様に表示されます。
これは「24:0a:c4:c5:46:fc」のノードが、Parent Nodeとして使われていて、自分はlayer 2であることを示します。
I (2161213) no_router: System information, channel: 11, layer: 2, self mac: 3c:71:bf:9d:bd:00, parent bssid: 24:0a:c4:c5:46:fd, parent rssi: -76, free heap: 204524
MeshLite nodes 2:
1: 1, 24:0a:c4:c5:46:fc, 0.0.0.0
2: 2, 3c:71:bf:9d:bd:00, 192.168.5.2

Mesh-Networkとしては以下の構成となります。
+-----------------+       +-----------------+
|24:0a:c4:c5:46:fc|-------|3c:71:bf:9d:bd:00|
+-----------------+       +-----------------+



次に3台目のESP32を非Root Deviceとしてビルドし、Root Deviceのそばに設置します。
RootのESP32には以下の様に表示されます。
これは「c8:c9:a3:cf:10:c4」のノードが、Child NodeとしてMesh-Networkに組み込まれたことを示しています。
I (91212) no_router: System information, channel: 11, layer: 1, self mac: 24:0a:c4:c5:46:fc, parent bssid: 00:00:00:00:00:00, parent rssi: -120, free heap: 201824
I (91216) no_router: Child mac: 3c:71:bf:9d:bd:00
I (91222) no_router: Child mac: 48:e7:29:b3:fb:08
MeshLite nodes 3:
1: 2, c8:c9:a3:cf:10:c4, 192.168.5.3
2: 2, 3c:71:bf:9d:bd:00, 192.168.5.2
3: 1, 24:0a:c4:c5:46:fc, 0.0.0.0

この時、2台目のESP32には以下の様に表示されます。
これは「c8:c9:a3:cf:10:c4」のノードが、自分と同じlayerでMesh-Networkに組み込まれたことを 示しています。
I (111278) no_router: System information, channel: 11, layer: 2, self mac: 3c:71:bf:9d:bd:00, parent bssid: 24:0a:c4:c5:46:fd, parent rssi: -74, free heap: 205056
MeshLite nodes 3:
1: 2, c8:c9:a3:cf:10:c4, 192.168.5.3
2: 1, 24:0a:c4:c5:46:fc, 0.0.0.0
3: 2, 3c:71:bf:9d:bd:00, 192.168.5.2

Mesh-Networkとしては以下の構成となります。
+-----------------+       +-----------------+
|24:0a:c4:c5:46:fc|---+---|3c:71:bf:9d:bd:00|
+-----------------+   |   +-----------------+
                      |
                      |   +-----------------+
                      +---|c8:c9:a3:cf:10:c4|
                          +-----------------+



3台目のESP32を少し離れた場所に移動します。
RootのESP32には以下の様に表示されます。
これは「c8:c9:a3:cf:10:c4」のノードが、layer 2からlayer 3に移動したことを示しています。
I (241212) no_router: System information, channel: 11, layer: 1, self mac: 24:0a:c4:c5:46:fc, parent bssid: 00:00:00:00:00:00, parent rssi: -120, free heap: 201824
I (241217) no_router: Child mac: 3c:71:bf:9d:bd:00
I (241222) no_router: Child mac: 48:e7:29:b3:fb:08
MeshLite nodes 3:
1: 3, c8:c9:a3:cf:10:c4, 192.168.4.2
2: 2, 3c:71:bf:9d:bd:00, 192.168.5.2
3: 1, 24:0a:c4:c5:46:fc, 0.0.0.0

2台目のESP32には以下の様に表示されます。
これは「c8:c9:a3:cf:10:c4」のノードが、layer 2からlayer 3に移動したことを示しています。
I (261278) no_router: System information, channel: 11, layer: 2, self mac: 3c:71:bf:9d:bd:00, parent bssid: 24:0a:c4:c5:46:fd, parent rssi: -74, free heap: 202188
I (261283) no_router: Child mac: 48:e7:29:b3:fb:08
MeshLite nodes 3:
1: 3, c8:c9:a3:cf:10:c4, 192.168.4.2
2: 1, 24:0a:c4:c5:46:fc, 0.0.0.0
3: 2, 3c:71:bf:9d:bd:00, 192.168.5.2

3台目のESP32には以下の様に表示されます。
I (191208) no_router: System information, channel: 11, layer: 3, self mac: c8:c9:a3:cf:10:c4, parent bssid: 3c:71:bf:9d:bd:01, parent rssi: -50, free heap: 206416
MeshLite nodes 3:
1: 1, 24:0a:c4:c5:46:fc, 0.0.0.0
2: 2, 3c:71:bf:9d:bd:00, 192.168.5.2
3: 3, c8:c9:a3:cf:10:c4, 192.168.4.2

Mesh-Networkとしては以下の構成となります。
これは「24:0a:c4:c5:46:fc」よりも、「3c:71:bf:9d:bd:00」の方が電波強度が強い(近くにある)ためです。
+-----------------+       +-----------------+      +-----------------+
|24:0a:c4:c5:46:fc|-------|3c:71:bf:9d:bd:00|------|c8:c9:a3:cf:10:c4|
+-----------------+      +-----------------+      +-----------------+



2台目のESP32の電源を落とします。
しばらく放置すると、RootのESP32から「3c:71:bf:9d:bd:00」のノードが消滅し、以下の様に表示され ます。
これは「3c:71:bf:9d:bd:00」のノード消滅に伴い、「c8:c9:a3:cf:10:c4」のノードが、 layer 3からlayer 2に移動したことを示しています。
ノードの消滅を検出するまでには数分の時間が掛かります。
I (761200) no_router: System information, channel: 11, layer: 1, self mac: 24:0a:c4:c5:46:fc, parent bssid: 00:00:00:00:00:00, parent rssi: -120, free heap: 203840
I (761204) no_router: Child mac: c8:c9:a3:cf:10:c4
MeshLite nodes 2:
1: 2, c8:c9:a3:cf:10:c4, 192.168.5.3
2: 1, 24:0a:c4:c5:46:fc, 0.0.0.0

3台目のESP32には以下の様に表示されます。
こちらも自分のノードが、layer 3からlayer 2に移動したことを示しています。
ノードの消滅を検出するまでに数分の時間が掛かります。
I (871208) no_router: System information, channel: 11, layer: 2, self mac: c8:c9:a3:cf:10:c4, parent bssid: 24:0a:c4:c5:46:fd, parent rssi: -90, free heap: 206240
MeshLite nodes 2:
1: 1, 24:0a:c4:c5:46:fc, 0.0.0.0
2: 2, c8:c9:a3:cf:10:c4, 192.168.5.3

Mesh-Networkとしては以下の構成となります。
+-----------------+       +-----------------+
|24:0a:c4:c5:46:fc|-------|c8:c9:a3:cf:10:c4|
+-----------------+      +-----------------+

この様にノードの場所やノードの有無により、自動的にNetwork構成が変化します。
この技術は、WiFiの電波が届かない場所、届きにくい場所にESP32を設置する場合に、中間にESP32を設置することでWiFi接続を可能 にすることを目的としています。



3台目のESP32の電源を落とします。
しばらく放置すると、RootのESP32から「c8:c9:a3:cf:10:c4」のノードが消滅し、以 下の様に表示され ます。
ノードの消滅を検出するまでには数分の時間が掛かります。
I (2101200) no_router: System information, channel: 11, layer: 1, self mac: 24:0a:c4:c5:46:fc, parent bssid: 00:00:00:00:00:00, parent rssi: -120, free heap: 205764
MeshLite nodes 1:
1: 1, 24:0a:c4:c5:46:fc, 0.0.0.0



こ のサンプルはMESH WiFiの基本的な機能を理解するには役に立ちますが、あまり実用的ではありません。
そこで、より実用的なこ ちらのサンプルを試してみます。
このサンプルでは2台以上のESP32、WiFiルーター、python3が動作するホストマシンが必要になります。
ビルドに際しては、menuconfigを使ってWiFiルーターのSSIDとパスワード、python3が動作するホストマシンのIPアドレス を指定する必要が有ります。


ホストマシンでは以下のスクリプトを起動しておきます。
このスクリプトはクライアントからの待ち受けと接続だけをメイン処理で行い、接続後の通信はスレッドで行います。
#!/usr/bin/env python3
#-*- encoding: utf-8 -*-
import socket
import argparse
import threading

def on_new_client(clientsocket,addr):
    print("on_new_client")
    while True:
        msg = clientsocket.recv(1024)
        #do some checks and if msg == someWeirdSignal: break:
        print("msg={}".format(msg))

    clientsocket.close()

if __name__=='__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--port', type=int, help='tcp port', default=8080)
    args = parser.parse_args()
    print("port={}".format(args.port))

    listen_num = 5
    #server_ip = "0.0.0.0"
    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    #tcp_server.bind((server_ip, args.port))
    tcp_server.bind(('', args.port))
    tcp_server.listen(listen_num)

    while True:
        client,address = tcp_server.accept()
        print("Connected from {}".format(address))
        thread = threading.Thread(target=on_new_client, args=(client,address,),daemon = True)
        thread.start()
    client.close()


pythonスクリプト起動時の引数として待ち受けするポート番号(8070)を指定します。
$ python3 tcp-server-threading.py --port 8070
port=8070

1台目のESP32をビルドします。
pythonスクリプトにESP32から受信したメッセージが表示されます。
ESP-Mesh-Liteでは、指定がない限り、最初に電源が投入されたデバイスがルートノードとなります。
$ python3 tcp-server-threading.py --port 8070
port=8070
Connected from ('192.168.10.131', 56549)
on_new_client
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 0}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 1}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 2}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 3}\r\n'

Mesh-Networkとしては以下の構成となります。
+-----------------+       +-----------------+
|   WiFi Router   |---+---|   Host(python)  |
+-----------------+   |   +-----------------+
                      |
                      |   +-----------------+
                      +---|24:0a:c4:c5:46:fc|
                          +-----------------+

2台目のESP32をビルドし、1台目とは少し離れた場所に設置します。
pythonスクリプトに1台目のESP32と2台目のESP32から受信したメッセージが表示されます。
2台目からのメッセージは1台目(192.168.10.131)を経由していることが分かります。
2台目のESP32のlayerは2となります。
Connected from ('192.168.10.131', 54539)
on_new_client
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 129}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 2,"count": 0}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 130}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 2,"count": 1}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 131}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 2,"count": 2}\r\n'

Mesh-Networkとしては以下の構成となります。
+-----------------+       +-----------------+
|   WiFi Router   |---+---|   Host(python)  |
+-----------------+   |   +-----------------+
                      |
                      |   +-----------------+      +-----------------+
                      +---|24:0a:c4:c5:46:fc|------|3c:71:bf:9d:bd:00|
                          +-----------------+      +-----------------+

3台目のESP32をビルドし、1台目、2台目とは少し離れた場所に設置します。
pythonスクリプトに1台目、2台目、3台目のESP32から受信したメッセージが表示されます。
3台目のESP32のlayerは3となります。
Connected from ('192.168.10.131', 52550)
on_new_client
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 2,"count": 47}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 177}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 3,"count": 0}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 2,"count": 48}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 178}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 3,"count": 1}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 2,"count": 49}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 179}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 3,"count": 2}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 2,"count": 50}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 180}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 3,"count": 3}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 2,"count": 51}\r\n'
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 181}\r\n'

Mesh-Networkとしては以下の構成となります。
これで、WiFiルーターから離れた場所に設置されているESP32も、WiFiルーター経由でホストマシンと通信することが出来るようになりま す。
+-----------------+       +-----------------+
|   WiFi Router   |---+---|   Host(python)  |
+-----------------+   |   +-----------------+
                      |
                      |   +-----------------+      +-----------------+      +-----------------+
                      +---|24:0a:c4:c5:46:fc|------|3c:71:bf:9d:bd:00|------|c8:c9:a3:cf:10:c4|
                          +-----------------+      +-----------------+      +-----------------+

1台目、2台目のESP32のロギングには、自分のアドレスと、Child Nodeのアドレスが表示されます。
I (761234) local_control: System information, channel: 11, layer: 1, self mac: 24:0a:c4:c5:46:fc, parent bssid: f8:b7:97:36:de:52, parent rssi: -80, free heap: 198604
I (761239) local_control: Child mac: 3c:71:bf:9d:bd:00

I (401241) local_control: System information, channel: 11, layer: 2, self mac: 3c:71:bf:9d:bd:00, parent bssid: 24:0a:c4:c5:46:fd, parent rssi: -73, free heap: 198732
I (401246) local_control: Child mac: c8:c9:a3:cf:10:c4

1台目のESP32の電源を落とします。
しばらくすると、pythonスクリプトに2台目、3台目のESP32から受信したメッセージが表示されます。
2台目のESP32がルートノードに変わります。
msg=b'{"src_addr": "24:0a:c4:c5:46:fc","data": "Hello TCP Server!","level": 1,"count": 46}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 2,"count": 28}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 3,"count": 13}\r\n'
Connected from ('192.168.10.115', 64421)
on_new_client
Connected from ('192.168.10.115', 53937)
on_new_client
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 1,"count": 33}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 2,"count": 17}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 1,"count": 34}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 2,"count": 18}\r\n'

Mesh-Networkとしては以下の構成となります。
+-----------------+       +-----------------+
|   WiFi Router   |---+---|   Host(python)  |
+-----------------+   |   +-----------------+
                      |
                      |   +-----------------+      +-----------------+
                      +---|3c:71:bf:9d:bd:00|------|c8:c9:a3:cf:10:c4|
                          +-----------------+      +-----------------+

2台目のESP32の電源を落とします。
しばらくすると、pythonスクリプトに3台目のESP32から受信したメッセージが表示されます。
3台目のESP32がルートノードに変わります。
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 2,"count": 12983}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 1,"count": 12999}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 2,"count": 12984}\r\n'
msg=b'{"src_addr": "3c:71:bf:9d:bd:00","data": "Hello TCP Server!","level": 1,"count": 13000}\r\n'
Connected from ('192.168.10.117', 53938)
on_new_client
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 1,"count": 12989}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 1,"count": 12990}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 1,"count": 12991}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 1,"count": 12992}\r\n'
msg=b'{"src_addr": "c8:c9:a3:cf:10:c4","data": "Hello TCP Server!","level": 1,"count": 12993}\r\n'

Mesh-Networkとしては以下の構成となります。
+-----------------+       +-----------------+
|   WiFi Router   |---+---|   Host(python)  |
+-----------------+   |   +-----------------+
                      |
                      |   +-----------------+
                      +---|c8:c9:a3:cf:10:c4|
                          +-----------------+



コンポーネントの中身を少しだけ調べてみました。
esp_mesh_lite_init()を呼び出すと、以下のロギングが表示されます。
内部でespnowを使っているようです。
I (1226) Mesh-Lite: esp-mesh-lite component version: 1.0.1
I (1232) ESPNOW: espnow [version: 1.0] init
I (1236) [vendor_ie]: Mesh-Lite commit id: 2660e63

I (1242) [vendor_ie]: Mesh ID: 77
W (1247) wifi:Haven't to connect to a suitable AP now!
I (1252) [ESP_Mesh_Lite_Comm]: msg action add success
I (1258) [ESP_Mesh_Lite_Comm]: Bind Socket 54, port 6364
I (1263) [ESP_Mesh_Lite_Comm]: Bind Socket 55, port 6363
I (1269) [ESP_Mesh_Lite_Comm]: Bind Socket 56, port 6366
I (1275) [ESP_Mesh_Lite_Comm]: Bind Socket 57, port 6365
I (1282) [mesh-lite-espnow]: Start espnow task

esp_mesh_lite_init()のソースはこ ちらに有ります。
この関数の中でesp_mesh_lite_core_init()が呼び出されていて、esp_mesh_lite_core_init()の 中でespnowタスクが起動されています。
esp_mesh_lite_core_init()のソースは公開されていないので、これ以上の追跡は不可能です。

続く...