IP over ESP-NOW実装を作成

概要

前回はESP-NOWを使ってシリアル通信ができるようにしましたが、今回はIP通信を実装してみました。NetIFを使ってブリッジのESP32側で接続しているWiFiや有線LANをESP-NOW経由で通信することが可能です。

仕組み

シリアルと同じくEspNowBusを利用して、暗号化してあるESP-NOWのネットワークを構築します。その上にインターネットなどにアクセス可能なゲートウエイと、ESP-NOW経由で通信をするデバイスの役割にわけています。

デバイス側はESP-NOW経由で1420バイト程度のデータを送信可能で、ゲートウエイ側は受け取ったパケットをNATしてから、上位のインターフェースにルーティングする動きになります。

仕組みとしてはESP32が利用しているLightWeightIP(LwIP)の仕組みを利用しています。複数のNetIFが利用でき、デフォルトIFの指定とNAPTの有効化を行うと他のIFからの通信を転送してくれる仕組みがあります。

注意点としてはESP32でNetIFとしてネットワークを認識できている必要があります。たとえばLTEなどのセルラーモバイルなどはPPPで接続した場合にはNetIFになりますが、ATコマンドでHTTP取得などを利用している場合にはNetIFではなく、ESP32外のモデム無いでネットワーク関連の処理をしているので、パケットの転送には利用できません。

特に注意が必要なのがM5Stack社のM5-Ethernetライブラリです。こちらもNetIF経由ではなく、EthernetClientなどのアクセスをするためのクラスが提供されており、内部は軽いモデム内蔵のネットワークスタックを利用しています。通常はこれで問題ないのですが、NetIFを利用した転送などがこのままだと利用できません。

スケッチ例

EspNowBus/examples/IP at main ツキ tanakamasayuki/EspNowBus
Contribute to tanakamasayuki/EspNowBus development by creating an account on GitHub.

上記にあります。

01_DeviceBasic

#include <Arduino.h>
#include <EspNowIP.h>
#include <esp_netif.h>
#include <esp_wifi.h>

EspNowIP ip;

void setup()
{
  Serial.begin(115200);
  delay(200);

  EspNowIP::Config cfg;
  cfg.groupName = "espnow-ip-demo";
  cfg.mtu = 1420;
  cfg.autoJoinIntervalMs = 0;

  if (!ip.begin(cfg))
  {
    Serial.println("ip.begin failed");
  }
}

void loop()
{
  ip.poll();
  delay(10);
}

重要なところだね抜き出すと上記です。EspNowBusだとgroupNameがマッチする者同士でネットワークを組みます。autoJoinIntervalMsで自分からは募集せずに、ゲートウエイ側からの募集を待ちます。

ip.poll()を定期的に呼ぶとネットワークに接続します。この関数自体は定期呼び出しをしなくても内部のネットワーク系のアクセスで多少はフォローされますが、なるべく定期的に呼び出しすることがおすすめです。

02_DeviceConnectivityCheck

01と基本かわりませんが、PINGやDNS、NTP、HTTPアクセスの例です。

03_GatewayWiFiSTA

      EspNowIPGateway::Config cfg;
      cfg.groupName = "espnow-ip-demo";
      cfg.uplink = WiFi.STA.netif();
      cfg.mtu = 1420;
      // cfg.channel = 6;                  // Optional: fix ESP-NOW to the same channel as the STA uplink.
      // cfg.phyRate = WIFI_PHY_RATE_1M_L; // Optional: lower PHY rate example for longer range.
      gatewayStarted = gateway.begin(cfg);
      Serial.printf("gateway.begin -> %d\n", gatewayStarted);

Wi-Fi接続後にEspNowBusに参加しているコードです。groupNameを任意のものに変更して、デフォルトのUplinkをcfg.uplink = WiFi.STA.netif()で指定しています。これでデフォルトGW的な設定が可能です。

ただし、WiFiSTAは落とし穴がありまして、接続しているWi-FiアクセスポイントのチャンネルでしかESP-NOWが利用できなくなります。そのため、接続先のチャンネルを確認してからデバイス側のチャンネルも変更して起動する必要があります。

基本的にWi-Fiアクセスポイントのチャンネルは再起動時とかに空いている場所を探して変更することがあるので固定しにくいです。そのためWiFiSTAでの接続は検証以外ではおすすめしません。

そもそもWi-Fiが使える環境であればIP over ESP-NOWを使う必要もないはずです。

04_GatewayEthernet

  ETH.begin(kEthType, kEthAddr, kEthMdcPin, kEthMdioPin, kEthPowerPin, kEthClockMode);

まずはがんばってEthernetを起動させます。注意事項にあるようにNetIFとして認識させる必要があります。M5Stack社の製品はデフォルトライブラリを利用するとNetIFとして認識しません。私はLilyGo T-Internet-COMを利用して実験をしました。

ハードウエア的にはNetIFで認識させることもできそうですが、リセット用の配線がないためにソフトウエアリセットを行ってから初期化をするのが好ましそうです。

    EspNowIPGateway::Config cfg;
    cfg.groupName = "espnow-ip-demo";
    cfg.mtu = 1420;
    // cfg.channel = 6;                  // Optional
    // cfg.phyRate = WIFI_PHY_RATE_1M_L; // Optional
    cfg.uplink = ETH.netif();
    gatewayStarted = gateway.begin(cfg);
    Serial.printf("gateway.begin -> %d\n", gatewayStarted);

こちらも最終的にはcfg.uplink = ETH.netif()でアップリンクの指定をすればブリッジしてくれます。

05_GatewayPPPSerial

netif/ppp/pppos.hを利用した接続です。昔からあるPPPをシリアル経由で接続します。これはUSBシリアルのパソコン側がUbuntuなどのpppdなどで待ち受けてきて、ESP32からPPPすることで通信をする形式です。

たとえばラズパイとかAIサーバーみたいなLinuxサーバーにUSB経由でESP32ゲートウエイを接続して、その間で通信をする想定です。ただし、接続実験までは出来ていません。がんばってPPPおSのNetIFが立ち上がればあとは他の方法と同じくアップリンクを指定するだけになります。

Windowsでも昔はダイアルアップ接続のサーバー側に慣れたのですが、ここ数年のWindows Updateで使えなくなっているようです。

06_GatewayPPPModem

LTEモデムなどATコマンドでPPPを実行する場合のサンプルです。こちらも実機では検証できていません。05との違いはATコマンドでまずは初期設定をしてからPPP接続を開始する必要があります。

ちなみにESP32だとこちらのセルラーPPPが一般的でPPPoSはマイナーになります。

こちらも一般的にはPPP接続をするのではなく、ATコマンドでHTTPとかMQTT通信をするパターンの方が多いのでNetIFで認識させるのは機種を選ぶと思います。

まとめ

基本的に実験的な実装となります。ユースケースはWi-Fiアクセスポイントが電波状況が悪くて使えない場合を想定しています。ただしESP-NOWはデフォルトでは送信到達チェックをしています。そしてEspNowBusではさらに暗号化したデータがちゃんと届いたのかのアプリレイヤでの送信チェックをしていますので1パケットの送信だけで2往復の通信が必要となります。

TCP/IPのようにプロトコルで送信確認をする場合にはオーバーヘッドが大きいので、スループット的にはかなり遅いです。通常の通信ができない環境で、遅くてもいいから普段と同じようなコードで通信をするための実装例となります。

ちなみにデバイス側からはEthernetClientなどを利用することで、有効になっているNetIFを適切に判定して使ってくれるのでIFを選択する必要はありません。

本当はESP32-S3だとUSBデバイスとしてNIC的にPCに認識させて、そのまま通信ってのが便利そうなのですが大変なので一旦ここで公開しました。

コメント